編寫線程安全的代碼時非洲,我們不希望對每次內(nèi)存訪問都進行分析一確保程序是線程安全的撩鹿,而是希望將一些現(xiàn)有的安全組建組合為更大規(guī)模的組建或程序浪读。下面介紹一些組合模式流译,這些模式能夠使一個類更容易成為線程安全的封字,并且在維護這些類是不會無意中破壞類的安全性保證的黔州。
設(shè)計線程安全的類
在設(shè)計線程安全類的過程中,需要包含以下三個基本要素:
- 找出構(gòu)成對象狀態(tài)的所有變量阔籽。
- 找出約束狀態(tài)變量的不變性條件流妻。
- 建立對象狀態(tài)的并發(fā)訪問管理策略。
對象的狀態(tài)是由對象的域組成的笆制,有0-n個不等绅这,如果域都是基本類型,那這些域構(gòu)成對象的全部狀態(tài)在辆,如果有引用類型证薇,那么該對象的狀態(tài)包括被引用對象的域(如LinkedList的狀態(tài)包括鏈表中所有節(jié)點對象的狀態(tài))度苔。
實例封裝
如果對象不是線程安全的額,那么可以通過多種技術(shù)使他在多線程程序中安全的使用浑度。
- 可以確保該對象只能由單個線程訪問(線程封閉)寇窑,如JDBC Connection對對象,ThreadLocal箩张。
- 通過一個鎖來保護該對象的所有訪問(Java監(jiān)視器模式)甩骏,如PersonSet,代碼在后面先慷。
- 封裝對象饮笛,只暴露可訪問的方法。與對象由整個程序訪問的情況比论熙,更容易對代碼進行分析缎浇。如Collections中的UnmodifiableCollection。
// mySet不會逸出赴肚,唯一的外部引用就是PersonSet,使用Java監(jiān)視器來封裝能確保線程安全二蓝。
@ThreadSafe
public class PersonSet {
@GuardedBy("this")
private final Set<Person> mySet = new HashSet<Person>();
public synchronized void addPerson(Person p) {
mySet.add(p);
}
public synchronized boolean containsPerson(Person p) {
return mySet.contains(p);
}
}
將數(shù)據(jù)封裝在對象內(nèi)部誉券,可以將數(shù)據(jù)的訪問限制在對象的方法上,從而更容易確保線程的訪問數(shù)據(jù)室總能持有正確的鎖刊愚。
線程安全性的委托
我們可以把線程安全委托給先有的線程安全類踊跟,這樣我們的代碼阿九不用關(guān)心線程安全的問題了額。這里有兩種情況:
- 如果委托給單獨的線程安全類鸥诽,能保證線程安全商玫。如,我們可以使用ConcurrentHashMap保存線程共享數(shù)據(jù)牡借。
- 如果委托給兩個或兩個已上的線程安全類拳昌,如果存在競態(tài)條件,需要額外的同步機制保證钠龙;如果分別表示獨立的狀態(tài)炬藤,可以不使用額外的同步機制即可保證線程安全。
- 在現(xiàn)有線程安全類中添加功能碴里,叫作客戶端加鎖沈矿。這種機制是派生類的行為與基類耦合在一起,破壞了基類的同步策略咬腋,使用時需要特別小心羹膳。
示例代碼:
// 情況2,需要增加同步機制保證 check-than-act
public class NumberRange {
// INVARIANT: lower <= upper
private final AtomicInteger lower = new AtomicInteger(0);
private final AtomicInteger upper = new AtomicInteger(0);
public void setLower(int i) {
// Warning -- unsafe check-then-act , need a lock
if (i > upper.get())
throw new IllegalArgumentException(
"can't set lower to " + i + " > upper");
lower.set(i);
}
public void setUpper(int i) {
// Warning -- unsafe check-then-act , need a lock
if (i < lower.get())
throw new IllegalArgumentException(
"can't set upper to " + i + " < lower");
upper.set(i);
}
public boolean isInRange(int i) {
return (i >= lower.get() && i <= upper.get());
}
}
小結(jié)
這一章介紹了實現(xiàn)線程安全類是采用的一些技術(shù)根竿。
- 線程安全可以委托給現(xiàn)有的線程安全類陵像。
- 委托是創(chuàng)建線程安全的一個有效策略就珠。
- 值需要讓現(xiàn)有的線程安全類管理所有的狀態(tài)即可
- 當需要使用多個線程安全類保存狀態(tài)是,需要額外的同步機制保證蠢壹。