對象的組合(第四章)

對象的組合

在設計線程安全類的過程當中,須要包含如下三個基本要素:
    1. 找出構成對象狀態的全部變量
    2. 找出約束狀態變量的不變性條件
    3. 創建對象狀態的併發訪問管理策略
  • 當對象的下一個狀態須要依賴當前狀態時,這個操做就必須是一個複合操做。若有一個Counter類,當前狀態爲17,那麼下一個狀態只能是18
  • 相關變量的讀取和更新必須在單個原子操做中進行
  • 若是某個操做中包含有基於狀態的先驗條件,那麼這個操做就稱爲依賴狀態的操做。如刪除一個隊列裏的元素,這個隊列當前必須是「非空」的
  • 封閉機制更易於構造線程安全的類

1.Java監視器模式

遵循Java監視器模式的對象會把對象的全部可變狀態都封裝起來,並由對象本身的內置鎖來保護 如下代碼是對象使用私有鎖的例子:安全

public class PrivateLock {
	private final Object myLock = new Object();
 	public void doSomething() {
    	synchronized(myLock) {...}
    }   
}

2.線程安全性的委託

委託是建立線程安全類的一個有效手段:只需讓現有的線程安全類管理全部的狀態便可。 經過多個線程安全類組合而成的類,其線程安全性視狀況而定。併發

  1. 對象中有單個線程安全對象是線程安全的
  2. 若是一個類是由多個獨立且線程安全的狀態變量組成的,而且在全部的操做中都不包含無效狀態轉換,那麼能夠將線程安全性委託給底層的狀態變量
  3. 若是類中多個變量之間存在着必定的不變性條件,那麼這個類就不是線程安全的,須要採起同步機制
  4. 若是一個狀態變量是線程安全的,而且沒有任何不變性條件來約束它的值,在變量的操做上也不存在不容許的狀態轉換,那麼就能夠安全地發佈這個變量

3.在現有的線程安全類中添加功能

要在現有的類中添加線程安全操做,有兩種方法:線程

  1. 修改原始類的代碼
  2. 擴展這個類。一下代碼擴展了Vector
public class BetterVector<E> extends Vector<E> {
	public synchronized boolean putIfAbsent(E x) {
    	boolean absent = !contains(x);
        if (absent) {add(x);}
        return absent;
    }
}

擴展的方法比直接修改源代碼要脆弱,由於同步策略實現被分佈到了多個單獨維護的源代碼文件中。若是底層的類改變了同步策略並選擇了不一樣的鎖來保護它的狀態變量,那麼子類會被破壞。 3. 客戶端加鎖機制 客戶端加鎖機制是指,對於使用某個對象X的客戶端代碼,使用X自己用於保護其狀態的鎖來保護這段客戶代碼。要使用客戶端加鎖機制,必須知道對象X使用的是哪個鎖。以下代碼中,第一個實現版本使用了不一樣的鎖,因此其並非原子的。設計

public class ListHelper<E> {
	public List<E> list = Collections.synchronizedList(new ArrayList<E>());
    //verson 1
    public synchronized boolean putIfAbsent(E x) {
    	boolean absent = !list.contains(x);
        if (absent) list.add(x);
        return absent;
    }
    //verson 2
    public boolean putIfAbsent(E x) {
    	synchronized(list) {//使用list本身的鎖
        	boolean absent = !list.contains(x);
        	if (absent) list.add(x);
        	return absent;
        }
    }
}
  1. 組合 爲現有類添加一個原子操做時,更好的方法是:組合(現有類做爲對象的私有final域)

4.將同步策略文檔化

在文檔中說明客戶代碼須要瞭解的線程安全性保證,以及代碼維護人員須要瞭解的同步策略code

相關文章
相關標籤/搜索