委托是創(chuàng)建線程安全類的一個最有效的策略, 只需讓現(xiàn)有的線程安全類管理所有的狀態(tài)即可
Collections.synchronizedXXX是一些同步容器類, 這些類的實現(xiàn)方式是將它們的狀態(tài)封裝起來, 并且對每個公有方法都進行同步
同步容器類包括 Vector, HashTable, Collections.synchronizedXXX
-
同步容器類的問題
(1) 同步容器類是線程安全的, 但是在執(zhí)行復(fù)合操作時仍需要客戶端加鎖
復(fù)合操作有:
1° 迭代
2° 跳轉(zhuǎn)(根據(jù)指定順序找到當(dāng)前元素的下一個元素)
3° 條件運算(若容器中不存在某個元素, 則添加這個元素)
示例
public class SafeVectorHelpers {
public static Object getLast(Vector list) {
int lastIndex = list.size() - 1;
return list.get(lastIndex);
}
public static void deleteLast(Vector list) {
int lastIndex = list.size() - 1;
list.remove(lastIndex);
}
}
當(dāng)有兩個線程分別對同一個Vector容器執(zhí)行g(shù)etLast和deleteLast函數(shù)時, 一個執(zhí)行完get(),另一個執(zhí)行remove就會報錯
(2) 為了保證復(fù)合操作的線程安全性就要加鎖, 此時符合"客戶端加鎖"的條件:找到同步容器使用了哪一個鎖,并對它加鎖
-
迭代器的迭代問題
(1) 無論顯式用迭代器迭代還是用for-each語法, 本質(zhì)都是使用Iterator迭代
(2) 容器使用了及時失敗機制: 當(dāng)發(fā)現(xiàn)容器在迭代過程中被外部更改或其他線程更改時, 報ConcurrentModification異常
(3) 防止容器報ConcurrentModification異常的手段
1° 加鎖
2° 先克隆容器, 再在克隆容器上迭代(克隆過程也要加鎖)
兩種手段都會使性能下降, 綜合考慮各種因素才能比較出兩種手段在不同場合下的優(yōu)劣
(3) 隱藏迭代器
很多時候方法內(nèi)部也使用了迭代, 但是隱藏了起來, 這時不注意的話就有多線程迭代異常的問題
示例
public class HiddenIterator { @GuardedBy("this") private final Set<Integer> set = new HashSet<Integer>(); public synchronized void add(Integer i) { set.add(i); } public synchronized void remove(Integer i) { set.remove(i); } public void addTenThings() { Random r = new Random(); for (int i = 0; i < 10; i++) { this.add(r.nextInt()); } System.out.println(this.set); } }
這里的addTenThings函數(shù)的最后一行System.out.println(this.set)會隱式調(diào)用set的迭代器, 將set中的所有元素轉(zhuǎn)換為String, 如果此時其他線程調(diào)用了add或remove函數(shù), 就會出現(xiàn)迭代異常