collections.synchronizedxxx方法仅保证单个操作线程安全,不支持复合操作原子性;迭代需手动同步mutex,否则抛concurrentmodificationexception;高并发场景应优先选用concurrenthashmap、copyonwritearraylist等并发集合。

Java中Collections.synchronizedXXX系列方法(如synchronizedList、synchronizedMap等)提供了一种快速包装线程安全集合的方式,但它们**只保证单个操作的原子性,不保证复合操作的线程安全性**——这是最容易被误用的关键点。
为什么synchronizedXXX不是“万能锁”
这些方法返回的是一个装饰器(Decorator),内部用同步块包裹每个公共方法。比如synchronizedList调用get()或add()时会自动加锁,但像if (!list.contains(x)) list.add(x)这样的判断+修改组合,两个方法之间锁已释放,存在竞态条件。
- 锁对象是集合包装器内部持有的一个
mutex(通常是自身或显式传入的对象),不是原始集合 - 迭代时必须手动同步:直接用
for-each或iterator()会抛ConcurrentModificationException,因为迭代过程未受保护 - 所有客户端代码必须使用同一个包装后的引用,若混用原始集合或多个包装实例,同步失效
正确使用迭代与复合操作
对同步集合做遍历或条件更新,必须显式同步在它的mutex上:
List<string> syncList = Collections.synchronizedList(new ArrayList());
// ✅ 正确:手动同步mutex
synchronized (syncList) {
Iterator<string> it = syncList.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
// ❌ 错误:隐式迭代触发CME
for (String s : syncList) { ... }</string></string>
类似地,putIfAbsent这类逻辑应改用ConcurrentHashMap等原生支持的并发集合,或自己加锁封装。
和java.util.concurrent替代方案对比
Collections.synchronizedXXX适合读多写少、逻辑简单且已有代码迁移成本低的场景;但高并发下性能较差(整个集合一把锁),推荐优先考虑更现代的替代:
-
CopyOnWriteArrayList:适用于读极多、写极少,且允许迭代期间写操作不阻塞读的场景 -
ConcurrentHashMap:分段锁或CAS实现,支持高并发读写,提供computeIfAbsent等原子复合操作 -
BlockingQueue实现类(如LinkedBlockingQueue):适合生产者-消费者模型
注意:ConcurrentHashMap不抛ConcurrentModificationException,其迭代器弱一致性,但不能反映实时全部变更。
实际建议:何时用、怎么用
除非明确需要轻量级包装已有集合且并发压力低,否则不建议新项目首选synchronizedXXX。若必须使用:
- 始终通过返回的包装引用访问集合,避免暴露底层非同步实例
- 所有复合操作(检查后插入、遍历中删除等)务必手动同步在集合的
mutex上 - 避免在同步块内调用外部可变对象的方法(防止锁顺序颠倒或死锁)
- 测试时模拟多线程竞争路径,尤其关注
size()+get(i)、isEmpty()+remove()这类典型陷阱
不复杂但容易忽略细节,关键在于理解它只解决“单步安全”,而非“业务逻辑安全”。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











