// 1. 索引遍歷(不推薦,LinkedList 性能差)
for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); }
// 2. Iterator 顯式遍歷
Iterator it = list.iterator();
while (it.hasNext()) { System.out.println(it.next()); }
// 3. 增強 for 循環(本質同 Iterator)
for (Object item : list) { System.out.println(item); }
// 4. Java 8 forEach + 匿名類
list.forEach(new Consumer
方式 2/3 本質相同;方式 4/5 本質相同。
ConcurrentModificationExceptionListlist = new ArrayList<>(); // 初始化 0-4 new Thread(() -> { // 遍歷線程 for (int item : list) { System.out.println("遍歷: " + item); Thread.sleep(1000); } }).start(); new Thread(() -> { // 修改線程 Thread.sleep(1000); list.remove(4); System.out.println("list.remove(4)"); }).start();
輸出:
遍歷: 0 遍歷: 1 list.remove(4) Exception in thread "Thread-0" java.util.ConcurrentModificationException
原因:遍歷時內部維護 modCount,修改操作導致 expectedModCount != modCount,拋出異常(fail-fast)。
Vector 單個方法同步,但無法鎖住整個遍歷過程。需客戶端加鎖:
Listlist = new Vector<>(); // 遍歷線程 synchronized (list) { for (int item : list) { ... } } // 修改線程無需加鎖,Vector 內部方法已同步,會等待鎖釋放
結果: 遍歷完成後才執行 remove,輸出 0-4 再打印 remove。
缺點: 遍歷期間阻塞所有寫操作,並發性極差;若遍歷耗時長,寫線程饑餓。
核心原理:寫時復制。寫操作(add/remove/set)會複製底層數組,修改副本,最後原子切換引用。遍歷時讀取的是快照,不受後續修改影響。
Listlist = new CopyOnWriteArrayList<>(); // 遍歷線程(無需加鎖) for (int item : list) { ... } // 修改線程 list.remove(4);
結果:
遍歷: 0 遍歷: 1 list.remove(4) 遍歷: 2 遍歷: 3 遍歷: 4 // 仍遍歷到已刪除的 4,因讀取的是舊快照
特點:
remove()、add() 等修改操作(拋 UnsupportedOperationException)forEach()(Java 8+ 隱式同步)Vector 重寫了 forEach 方法並加上 synchronized:
public synchronized void forEach(Consumer super E> action) { ... }
測試證明:list.forEach(item -> ...) 遍歷期間,remove 會等待遍歷完成。
Listlist = new Vector<>(); list.forEach(item -> { // 線程安全遍歷 System.out.println(item); Thread.sleep(1000); }); // 另一線程 list.remove(4) 將阻塞至遍歷結束
優勢: 代碼簡潔,無需顯式 synchronized 塊。
注意: ArrayList、CopyOnWriteArrayList 的 forEach 未加同步,非線程安全。
| 場景 | 推薦方案 | 核心理由 |
|---|---|---|
| 讀寫均衡、需強一致性 | Vector + synchronized 塊 / Collections.synchronizedList | 手動控制鎖範圍,保證遍歷與修改原子性 |
| 極度讀多寫少、容忍弱一致 | CopyOnWriteArrayList | 遍歷零鎖、高吞吐,寫入複製數組代價大但頻率低 |
| 代碼簡潔、Java 8+、Vector 既有代碼 | Vector.forEach() | 內部 synchronized,一行代碼實現線程安全遍歷 |
| 高並發 Map/Set 遍歷 | ConcurrentHashMap / ConcurrentSkipListMap | 弱一致性迭代器,無需加鎖,性能最優 |
forEach 只是語法糖,線程安全性取決於具體集合實現,切勿想當然