Collections.synchronizedCollection仅保证单个操作原子性,不保证复合操作线程安全;需手动同步检查-添加、迭代等场景,或改用ConcurrentHashMap等原生并发集合。

直接用 Collections.synchronizedCollection 包一层,**并不能让集合“自动线程安全”**——它只保证单个操作(如 add、remove)原子,但复合操作(如“检查是否存在再添加”)仍需手动同步。实战中若忽略这点,极易出现竞态条件。
明确它的能力边界:只保单步原子,不保逻辑原子
该方法返回一个装饰器(Decorator),内部对每个公有方法加了 synchronized(this)。例如:
Collection<string> syncList = Collections.synchronizedCollection(new ArrayList());
syncList.add("a"); // ✅ 线程安全(底层已同步)
</string>
但下面这段代码依然不安全:
if (!syncList.contains("b")) {
syncList.add("b"); // ❌ 两步之间可能被其他线程插入相同元素
}
因为 contains 和 add 是两次独立的同步调用,中间存在时间窗口。
必须显式同步的典型场景
以下操作不能依赖装饰器自身,需外层统一加锁:
- 检查-然后-操作(check-then-act):如先查后删、先判空再取首元素
- 迭代遍历:即使用了 synchronizedCollection,迭代器本身不是同步的,需手动同步整个遍历块
- 批量操作组合:如“清空并重新填充”“按条件批量移除”
正确写法示例(以遍历为例):
Collection<string> syncCol = Collections.synchronizedCollection(new ArrayList());
// ...
synchronized (syncCol) { // 锁住装饰器对象本身
for (String s : syncCol) {
System.out.println(s);
}
}
</string>
替代方案比强行加锁更实用
除非遗留系统强约束,否则优先考虑更现代、更安全的原生线程安全集合:
-
并发读多写少 → 用
CopyOnWriteArrayList或CopyOnWriteArraySet(迭代安全,写操作开销大) -
高并发增删查均衡 → 用
ConcurrentHashMap(keySet/entrySet 可安全迭代),或其衍生集合如ConcurrentLinkedQueue -
需要阻塞等待 → 用
BlockingQueue实现(如LinkedBlockingQueue)
它们在设计上就规避了“手动同步”的易错点,且性能通常优于全局锁。
真要用 synchronizedCollection?记住三个实操要点
-
锁对象必须是返回的装饰器实例本身,不要锁原始集合(如
new ArrayList()),否则不同步 - 所有访问路径必须经过该装饰器引用,避免有人绕过它直操作底层集合
-
注意迭代器失效风险:即使同步了遍历块,其他线程仍可能在遍历时修改集合(只是不会抛
ConcurrentModificationException,但结果不可预期)
本质上,它是线程安全的“最低成本适配层”,适合快速包装已有集合接口,但绝不应成为并发逻辑的设计起点。










