當一個執行緒正在遍歷集合(Iterator),另一執行緒修改該集合(增/刪元素),即拋出 ConcurrentModificationException。直接同步整個遍歷過程雖能解決,但若遍歷涉及複雜運算或 I/O,鎖持有時間過長會嚴重降低併發吞吐。
核心思想:在同步區塊內快速複製元素到新容器,釋放鎖後對副本進行唯讀遍歷。
private final List<Object> list = new ArrayList<>();
public void add(Object obj) {
synchronized (list) { list.add(obj); }
}
public void perform() {
Iterator<Object> snapshotIter;
synchronized (list) {
// 快照複製:僅複製引用,極快
snapshotIter = new CopiedIterator(list.iterator());
}
// 無鎖遍歷,可執行耗時操作
while (snapshotIter.hasNext()) {
process(snapshotIter.next());
}
}
public class CopiedIterator<E> implements Iterator<E> {
private final Iterator<E> delegate;
public CopiedIterator(Iterator<E> source) {
List<E> copy = new ArrayList<>();
while (source.hasNext()) copy.add(source.next());
this.delegate = copy.iterator();
}
@Override public boolean hasNext() { return delegate.hasNext(); }
@Override public E next() { return delegate.next(); }
@Override public void remove() { throw new UnsupportedOperationException("唯讀快照,不可移除"); }
}
| 特性 | 說明 |
|---|---|
| 執行緒安全 | 遍歷期間不持有原集合鎖,極大縮短臨界區 |
| 資料一致性 | 屬於「弱一致性」:遍歷見的是複製那一刻的快照,後續異動不可見 |
| 功能限制 | 不支援 remove(),非即時視圖 |
| 記憶體開銷 | O(N) 額外空間,N 為元素數量 |