Collections.synchronizedCollection仅保证单个方法原子性,复合操作需手动加锁;迭代必须同步块或快照遍历;推荐优先使用CopyOnWriteArrayList、ConcurrentHashMap等更优并发集合。

直接用 Collections.synchronizedCollection 包一层,不等于集合就“自动线程安全”了。它只保证单个方法(如 add、remove、contains)的原子性,而业务逻辑常需多个操作组合——这些复合场景必须手动加锁,否则极易出现竞态条件或 ConcurrentModificationException。
它能做什么:单步操作确实线程安全
该方法返回一个装饰器对象,对每个公有方法内部加 synchronized(this)。只要所有访问都走这个包装后的引用,以下操作本身是安全的:
syncCol.add("x")syncCol.remove("y")syncCol.size()syncCol.isEmpty()
注意:锁对象是装饰器实例自身,不是原始集合;若有人绕过它直接操作底层 new ArrayList(),同步即失效。
它不能做什么:复合操作必须外层同步
以下常见逻辑无法靠装饰器自动保障,必须显式用 synchronized(syncCol) 包裹整段代码:
立即学习“Java免费学习笔记(深入)”;
-
检查再操作:比如
if (!syncCol.contains("a")) syncCol.add("a")—— 中间存在时间窗口,另一线程可能抢先插入 -
判空后取值:如
if (!syncCol.isEmpty()) syncCol.iterator().next()—— 判空后、取元素前集合已被清空,抛NoSuchElementException -
迭代遍历:
for (E e : syncCol)底层调用的是未同步的迭代器,必须同步整个循环块 - 批量变更:例如“清空后批量添加”或“按条件逐个移除”,每一步单独安全,但整体不具原子性
遍历安全的两种可靠方式
迭代是最易出错的环节,推荐两种实践:
-
同步块遍历:适用于需实时看到最新数据、且操作轻量的场景
synchronized (syncCol) { for (String s : syncCol) { ... } } -
快照遍历:创建副本再遍历,避免锁竞争,适合读多写少、允许短暂延迟的场景
List<String> snapshot = new ArrayList<>(syncCol); for (String s : snapshot) { ... }
更优替代方案值得优先考虑
除非受限于旧系统或兼容性要求,否则建议避开 synchronizedCollection 的手动同步负担,选用设计更严谨的并发集合:
- 读远多于写 →
CopyOnWriteArrayList或CopyOnWriteArraySet(迭代绝对安全,写开销大) - 高并发均衡读写 →
ConcurrentHashMap(其keySet()、values()、entrySet()可安全迭代) - 需要阻塞等待或队列语义 →
LinkedBlockingQueue、ArrayBlockingQueue
它们从设计上规避了“单步安全但逻辑不安全”的陷阱,多数场景下性能也优于全局锁。


















