非同步併發修改檢測機制。集合內部維護 modCount(修改次數),迭代器建立時快照 expectedModCount = modCount。每次 next() 前比對,若不一致拋 ConcurrentModificationException(CME)。
// ArrayList.java
private transient int modCount = 0;
// Itr 迭代器
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
// 單執行緒也會觸發:迭代過程中結構性修改
List<Integer> list = new ArrayList<>(Arrays.asList(1,2,3));
Iterator<Integer> it = list.iterator();
list.add(4); // 結構性修改
it.next(); // 拋 CME
// 多執行緒:寫執行緒插入,讀執行緒遍歷
List<Integer> list = new ArrayList<>();
new Thread(() -> { while(true) { list.add(1); sleep(500); } }).start();
Iterator<Integer> it = list.iterator();
while (it.hasNext()) { it.next(); sleep(80); } // 幾乎必現 CME
| 方案 | 原理 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|---|
| 加鎖同步 | synchronized(list) 包裹遍歷 | 簡單、強一致性 | 鎖住整個遍歷期,併發度極低 | 讀寫極不平衡、遍歷極短 |
| Collections.synchronizedList | 裝飾器模式,方法級鎖 | API 兼容 | 迭代器仍需手動同步,易忘 | 輕量級同步 |
| CopyOnWriteArrayList | 寫時複製新陣列,讀舊陣列 | 讀完全無鎖、遍歷不拋 CME、適合讀多寫少 | 寫入開銷大(複製陣列)、記憶體雙倍、資料最終一致性 | 快取、配置、事件監聽器、黑白名單 |
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1); // 複製
newElements[len] = e; // 修改新陣列
setArray(newElements); // volatile 寫入,發布新陣列引用
return true;
} finally { lock.unlock(); }
}
// 迭代器構造器捕獲當前陣列引用
private static class COWIterator<E> implements ListIterator<E> {
private final Object[] snapshot; // 關鍵:不可變快照
private int cursor;
COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
snapshot = elements;
}
public E next() { return (E) snapshot[cursor++]; }
public boolean hasNext() { return cursor < snapshot.length; }
}
核心特性:
remove()/set()/add()(拋 UnsupportedOperationException)。getArray() 回傳當前最新陣列,get(index) 讀最新值。// 1000 元素,10 萬次讀(遍歷),100 次寫(add) CopyOnWriteArrayList: 讀 12ms / 寫 45ms synchronized ArrayList: 讀 180ms / 寫 220ms (鎖爭用) ArrayList (無同步): 讀 8ms / 寫 5ms (非線程安全)
讀多寫少(> 99% 讀)場景,COW 完勝。
ConcurrentHashMap 方案。if (!list.contains(x)) list.add(x) 非原子,仍需外層鎖。StampedLock 優化讀寫鎖、ConcurrentHashMap 做鍵值化存儲、Guava ImmutableList 靜態配置。| 場景 | 推薦集合 | 理由 |
|---|---|---|
| 單執行緒、高性能 | ArrayList | 零開銷 |
| 多執行緒、讀寫均衡 | ConcurrentHashMap / Collections.synchronizedList | 細粒度鎖 / 簡單同步 |
| 多執行緒、讀多寫少、遍歷頻繁 | CopyOnWriteArrayList | 讀無鎖、遍歷安全 |
| 佇列/生產消費 | ConcurrentLinkedQueue / ArrayBlockingQueue | 專用佇列語義 |
| 不可變配置 | ImmutableList / List.of() | 編譯期/執行期保證不可變 |