collections.synchronizedlist 不能自动保障迭代过程的线程安全,因增强 for 循环使用未同步的原始 iterator,需显式 synchronized(list) 包裹遍历;快照遍历或避免 sublist 等伪安全操作可提升安全性。

Collections.synchronizedList 不能自动保障迭代过程的线程安全,增强 for 循环(即隐式使用 Iterator)时若不加同步块,大概率触发 ConcurrentModificationException,或读到漏项、重复、状态不一致的数据。
为什么增强 for 循环会出问题
Java 中的 for-each 语法本质上是调用 iterator() 获取普通迭代器,再反复调用 hasNext() 和 next()。而 synchronizedList 的 iterator() 方法本身不加锁,返回的是底层 ArrayList 的原始迭代器——它只做快速失败(fail-fast)检查,不感知外部同步控制。
这意味着:即使 add/remove 等单步操作被同步保护,遍历过程中其他线程仍可插入修改,导致迭代器检测到结构变更后直接抛异常。
必须显式同步整个遍历逻辑
官方文档明确要求:所有通过 Iterator 或 for-each 访问 synchronizedList 的场景,都需用 synchronized(list) 包裹整段遍历代码。
- 正确写法:用列表实例本身作为锁对象,因为 synchronizedList 内部也以 this 为 mutex
- 错误写法:单独对某次
iterator()调用加锁,或用新创建的对象锁,无法同步后续next() - 示例:
synchronized (safeList) { for (String s : safeList) { // ✅ 安全 System.out.println(s); } }
更稳妥的替代方案:快照遍历
当遍历耗时较长、或需避免长时间持锁影响并发性能时,推荐先获取不可变快照:
List<string> snapshot = new ArrayList(safeList);</string>- 之后在同步块外遍历
snapshot,完全规避锁竞争和 CME 风险 - 适用于读多写少、允许短暂数据延迟的场景
容易被忽略的“伪安全”陷阱
以下操作看似用了 synchronizedList,实则失去同步保障:
-
safeList.subList(0, 5)→ 返回非同步子视图,所有操作无锁 -
Arrays.asList(safeList)→ 创建新 List,不继承同步行为 - 将原始 ArrayList 引用暴露给其他变量 → 同步代理形同虚设
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











