不能在synchronized块外直接遍历collections.synchronizedlist等同步包装器,因其迭代器不加锁,易导致concurrentmodificationexception或数据不一致;必须在synchronized(集合)块内完成遍历,或改用copyonwritearraylist等真正线程安全的并发集合。

不能在 synchronized 块外直接对普通线程安全集合(如 Collections.synchronizedList)做长生命周期遍历——因为其迭代器本身不加锁,一旦其他线程修改集合,就会触发 ConcurrentModificationException 或读到不一致状态。
为什么同步包装器的迭代器需要额外加锁
像 Collections.synchronizedList(new ArrayList()) 这类包装器,只保证 add()、get() 等单个方法调用的原子性。但 iterator() 返回的迭代器是“裸”的,内部未同步。遍历时若发生并发修改,快速失败机制会立即抛异常;即使没抛,也可能因缺乏内存可见性而看到过期数据。
关键点:迭代器不是“自动带锁”的组件,它只是对底层集合的一次快照式访问入口,是否安全取决于你如何控制它的生命周期和访问上下文。
在 synchronized 块内完成完整遍历是最直接方案
对长生命周期集合变量(如作为类成员长期存在、被多个线程反复读写的 List),推荐将整个遍历逻辑包裹在 synchronized 块中,并显式锁定该集合对象本身:
- 使用
synchronized (list) { ... },而非synchronized (this)或静态锁,确保锁粒度与数据范围一致 - 遍历前获取锁,遍历中不做耗时操作(如 I/O、远程调用),避免阻塞其他线程
- 若需边遍历边处理并移除元素,优先用迭代器的
remove()方法,它与当前同步块属于同一临界区
示例:
private final List<string> sharedList = Collections.synchronizedList(new ArrayList());<br>// ...<br>synchronized (sharedList) {<br> Iterator<string> it = sharedList.iterator();<br> while (it.hasNext()) {<br> String item = it.next();<br> if (shouldRemove(item)) {<br> it.remove(); // 安全,仍在锁保护下<br> } else {<br> process(item); // 处理逻辑应轻量<br> }<br> }<br>}</string></string>
替代方案:选用真正支持安全遍历的并发集合
如果遍历频率高、写入少,且能接受快照语义,CopyOnWriteArrayList 是更优解:
- 迭代器基于创建时刻的数组副本,遍历时完全无锁,不受其他线程写入影响
- 适合读多写少场景(如配置监听列表、事件处理器注册表)
- 注意:每次写操作复制整个数组,写性能差、内存开销大,不适合高频更新或大数据量
若需准实时遍历(读写可并发、不依赖快照),可考虑 ConcurrentHashMap 的 keySet/valueSet 迭代器——它采用分段锁+弱一致性设计,遍历时允许写入,但不保证看到最新变更。
自定义迭代器锁机制的实战要点
所谓“自定义迭代器锁”,本质是把锁对象从集合自身解耦出来,实现更灵活的同步控制:
- 声明私有 final 锁对象:
private final Object iterationLock = new Object(); - 所有涉及该集合的读(含遍历)、写操作,统一使用该锁同步,而非依赖集合自带锁
- 好处:避免与
Collections.synchronizedX的内置锁冲突;便于未来替换为ReentrantLock等高级锁 - 风险:必须严格约定所有协作者都遵守该锁协议,否则形同虚设
这种模式更适合封装成工具类或抽象基类,由团队统一维护,不建议在业务代码中零散手写。










