包裝器實現是一種將核心工作委託給特定集合對象,並在其功能基礎上增加額外行為的設計模式。對設計模式愛好者而言,這正是 Decorator(裝飾器)模式 的經典應用。雖然概念聽起來略顯「異國情調」,但實際運作極為直觀簡單。
這些實現類均為匿名類:JDK 不提供公共類,而是透過 Collections 工具類中的靜態工廠方法來獲取實例。所有相關方法均定義在 java.util.Collections 中。
同步包裝器能為任意集合自動添加線程安全特性。針對六大核心集合接口,Collections 提供了對應的靜態方法:
public static Collection synchronizedCollection(Collection c);
public static Set synchronizedSet(Set s);
public static List synchronizedList(List list);
public static Map synchronizedMap(Map m);
public static SortedSet synchronizedSortedSet(SortedSet s);
public static SortedMap synchronizedSortedMap(SortedMap m);
每個方法均返回一個由指定集合作為後備存儲的同步(線程安全)集合。關鍵點:為確保串行訪問,所有對後備集合的操作必須透過返回的同步集合完成。最簡單的做法是不保留對原始集合的引用,例如:
List list = Collections.synchronizedList(new ArrayList());
這樣創建的集合在單操作層面上是線程安全的,類似於早期的 Vector。然而,在並發環境下對返回集合進行迭代時,必須手動同步,因為迭代涉及多次方法調用,需作為單一原子操作執行。
Collection c = Collections.synchronizedCollection(myCollection);
synchronized(c) {
Iterator i = c.iterator(); // 必須在同步塊內
while (i.hasNext())
foo(i.next());
}
迭代同步 Map 的集合視圖(如 keySet()、values()、entrySet())時,必須鎖定 Map 本身,而非視圖對象:
Map m = Collections.synchronizedMap(new HashMap());
Set s = m.keySet(); // 無需同步塊
synchronized(m) { // 鎖定 m,而非 s
Iterator i = s.iterator(); // 必須在同步塊內
while (i.hasNext())
foo(i.next());
}
包裝器實現的一個小缺陷是無法調用被包裝對象特有的非接口方法。例如上例中,無法在同步 List 上調用 ArrayList 特有的 ensureCapacity() 方法。