編寫線程安全的代碼時,我們不希望對每次內(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)即可
- 當(dāng)需要使用多個線程安全類保存狀態(tài)是,需要額外的同步機制保證。