collections.synchronizedcollection仅保证单个操作线程安全,复合操作和迭代需手动同步;推荐优先使用concurrenthashmap等java.util.concurrent并发集合。

Collections.synchronizedCollection 是 Java 提供的线程安全包装工具,但它**只保单个操作安全,不保复合逻辑和迭代过程**。用它不是“开了锁就万事大吉”,而是得清楚它的边界在哪、哪些地方必须自己兜底。
单个方法调用是安全的
它把传入的原始集合(比如 ArrayList、HashSet)包装成一个代理对象,所有 public 方法(add、remove、size、contains 等)内部都加了 synchronized 锁,锁对象就是原始集合本身。这意味着:
- 两个线程同时调用 syncColl.add("x"),不会导致数据错乱或丢失
- syncColl.size() 和 syncColl.isEmpty() 这类读操作也受保护,返回结果是一致的快照
- 只要不绕过包装器直接操作底层集合,就不会破坏同步契约
复合操作必须手动加锁
像“先检查再添加”、“遍历中删除”这类多步逻辑,synchronizedCollection 不提供原子性保障:
- 写法错误:if (!syncColl.contains("a")) syncColl.add("a"); —— 中间可能被其他线程插入相同元素
- 正确做法:把整个判断+动作包进 synchronized 块,锁对象仍是 syncColl(或其底层集合)
- 常见场景还包括:批量添加、条件更新、统计后清空等,都要视为一个逻辑单元来同步
迭代必须显式同步
iterator()、toArray()、forEach() 这些方法本身不加锁,直接使用会出问题:
- 并发修改时可能抛 ConcurrentModificationException
- 也可能读到部分更新的中间状态(脏读),尤其在写频繁场景
- 正确方式是:synchronized (syncColl) { for (Object o : syncColl) { ... } }
- 注意:锁对象必须和包装器内部用的是同一个(即原始集合),否则不同步
更推荐用原生并发集合
除非维护老系统或有特殊兼容要求,否则优先考虑 java.util.concurrent 包里的实现:
- CopyOnWriteArrayList:适合读远多于写的场景,迭代不阻塞,写操作复制整个数组
- ConcurrentHashMap:分段锁或 CAS + synchronized 优化,支持高并发读写,get 几乎无锁
- BlockingQueue(如 LinkedBlockingQueue):专为生产者-消费者设计,自带等待/通知机制
- 它们不只是“加个锁”,而是从数据结构和算法层面做了并发适配,性能和安全性都更可靠
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











