一、設計線程安全的類
- 找出構造對象狀態的所有變量(若變量為引用類型,還包括引用對象中的域)
- 約束狀態變量的不變性條件
- 建立對象狀態的並發訪問管理策略(規定了如何維護線程安全性)
1、收集同步需求(找出復合操作、多個變量遵循原子性的操作等)
2、依賴狀態的操作(找出操作是否基於先驗條件,例:取出當隊列不為空)
3、狀態的所有權(對象被哪些線程所有,哪些線程可以操作對象)
二、實例封閉
將數據封裝在對象內部,可以將數據的訪問限制在對象的方法上,確保簡單正確的持有鎖。(因為無需觀察整個程序,只需檢查當前類)
Java提供包裝器對ArrayList、HashMap等容器對象提供線程安全保護
1、Java監視器模式:把對象的所有可變狀態封裝起來,並使用對象的內置鎖保護
使用私有鎖而不是內置鎖的優點:(private final Object myLock = new Object();)
- 私有鎖可以將鎖封裝起來,客戶代碼無法得到鎖
- 客戶可以通過公有方法來訪問鎖,以便參與到同步策略中去。
2、實例:基於監視器模式的車輛追蹤
MutablePoint不一定是線程安全的,但該類一定是線程安全的,因為它包含的Map對象與Map中的Point對象都未曾發布,並且滿足實例封閉。
三、線程安全性的委托
通過多個線程安全的類組成的類不一定是線程安全的
委托:通過委托類的線程安全性判斷被委托類的線程安全性
1、委托給單個線程安全狀態變量可以保證線程安全性
2、委托給多個相互獨立的線程安全狀態變量可以保證線程安全性
3、如果類包含多個線程安全狀態變量的符合操作,則無法保證線程安全性,可以通過加鎖機制保證
4、發布:如果一個狀態變量是線程安全的,並且沒有不變性條件約束它(例:大於0),在變量操作上沒有不允許的狀態轉換,則可以安全發布
5、安全發布底層狀態的線程安全類
1 @ThreadSafe 2 public class PublishingVehicleTracker { 3 private final Map<String, SafePoint> locations; 4 private final Map<String, SafePoint> unmodifiableMap; 5 6 public PublishingVehicleTracker(Map<String, SafePoint> locations) { 7 this.locations = new ConcurrentHashMap<String, SafePoint>(locations); 8 this.unmodifiableMap = Collections.unmodifiableMap(this.locations); 9 } 10 11 public Map<String, SafePoint> getLocations() { 12 return unmodifiableMap; 13 } 14 15 public SafePoint getLocation(String id) { 16 return locations.get(id); 17 } 18 19 public void setLocation(String id, int x, int y) { 20 if (!locations.containsKey(id)) 21 throw new IllegalArgumentException("invalid vehicle name: " + id); 22 locations.get(id).set(x, y); 23 } 24 }
四、現有的線程安全類中添加功能
例:在Vector中添加”若沒有則添加“功能,可以使用拓展的辦法,BetterVector繼承至Vector並對該符合操作通過同步機制增加原子性
1、客戶端加鎖機制
使用某個對象的代碼時必須使用該對象本身用於保護其狀態的鎖,不推薦(同步的實現被分到兩個不相關的類中)
當沒有使用同一個鎖,不足以提供線程安全保護:
2、組合
ImprovedList將List的操作委托給底層的list實例來操作,並通過自身的內置鎖增加一層額外的加鎖,同時添加了新的同步方法。
五、將同步策略文檔化