Collections.synchronizedXxx()仅保证单个方法调用线程安全,不支持复合操作原子性;迭代需手动同步;推荐优先使用ConcurrentHashMap、CopyOnWriteArrayList等并发集合。

Java中Collections.synchronizedXxx()方法本身是线程安全的,但**仅限于单个操作层面**——它不能保证复合操作的原子性,因此在实际多线程场景中容易出错,需谨慎使用。
同步包装器只保护单个方法调用
例如Collections.synchronizedList(new ArrayList())会对add()、get()、size()等每个方法加锁(内部使用对象自身作为锁),确保这些调用不会被并发干扰。但像“检查是否存在再添加”这种两步操作:
if (!list.contains(e)) list.add(e);
中间可能被其他线程插入相同元素,导致重复——因为contains()和add()是两次独立加锁操作,锁已释放。
迭代时必须手动同步外部锁
同步集合的iterator()、toArray()等方法**不自动加锁**。直接遍历时可能抛ConcurrentModificationException或读到脏数据:
立即学习“Java免费学习笔记(深入)”;
- 错误写法:
for (Object o : syncList) { ... }(隐式调用iterator(),无同步) - 正确写法:显式同步整个遍历块:
synchronized (syncList) { for (Object o : syncList) { ... } }
替代方案更推荐:使用并发集合
除非遗留系统受限,否则优先选用java.util.concurrent包中的原生并发集合:
-
CopyOnWriteArrayList:适合读多写少、允许迭代期间写入的场景 -
ConcurrentHashMap:支持高并发读写,分段锁或CAS优化,性能远超Collections.synchronizedMap() -
BlockingQueue实现类(如LinkedBlockingQueue):适用于生产者-消费者模型
锁对象细节影响协作安全性
所有Collections.synchronizedXxx()返回的包装器,其内部锁是传入的原始集合对象(如new ArrayList()实例)。这意味着:
- 若多个线程通过不同引用访问同一底层集合,且都用
synchronizedXxx()包装,它们仍共享同一把锁,能协同互斥 - 但若混用包装器与原始集合(如一个线程操作
syncList,另一个直接操作rawList),则完全失去同步保障










