synchronizedlist无法避免fail-fast,因其仅保证单个方法线程安全,迭代器不持锁且modcount检查在多线程下失效;正确做法是手动同步整个迭代过程或改用copyonwritearraylist等替代方案。

Collections.synchronizedList 不能避免 fail-fast,它只是让 list 的基础操作线程安全,但迭代过程仍可能抛 ConcurrentModificationException。
为什么 synchronizedList 无法解决 fail-fast
fail-fast 是 ArrayList、LinkedList 等集合在迭代过程中检测到结构被修改(如 add/remove)时主动抛出 ConcurrentModificationException 的机制。synchronizedList 仅对单个方法加锁(如 get、add、remove),但 迭代器本身不持有锁,且其内部的 modCount 检查在多线程下仍会失效。
例如:线程 A 调用 iterator() 获取迭代器后,线程 B 调用 add() 修改列表 → 线程 A 在 next() 时发现 modCount 不一致 → 抛异常。
正确做法:同步迭代过程
要安全遍历 synchronizedList,必须手动对整个迭代过程加锁,确保迭代期间无其他线程修改:
- 使用 synchronized 块包裹迭代逻辑,锁对象为 list 本身(因为 synchronizedList 返回的代理对象,其内部锁就是传入的 list)
- 避免在迭代中调用 remove() 或 add() —— 即使是 synchronizedList 的 remove(),也会破坏迭代器一致性
- 若需边遍历边删除,改用 Iterator.remove()(它与迭代器状态协同,但前提是整个迭代过程受同一把锁保护)
更推荐的替代方案
比起手动加锁 + synchronizedList,以下方式更健壮:
- CopyOnWriteArrayList:读操作无锁、写操作复制底层数组,天然规避 fail-fast,适合读多写少场景
- ConcurrentHashMap + 遍历 keySet/values:若业务可转为键值结构,ConcurrentHashMap 的迭代器弱一致性,不会抛 ConcurrentModificationException
- 显式锁 + 可重入锁控制读写分离:适用于复杂逻辑,但需自行管理锁粒度和条件
典型错误示例与修正
错误写法(看似安全,实则仍 fail-fast):
Listlist.add("a");
for (String s : list) { ... } // 这里仍可能抛异常
正确写法(显式同步迭代):
synchronized (list) {Iterator
while (it.hasNext()) {
System.out.println(it.next());
}
}
不复杂但容易忽略:synchronizedList 提供的是方法级线程安全,不是迭代安全。真正规避 fail-fast,关键在控制迭代期间的修改可见性,而非仅仅套一层同步包装。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











