collections.synchronizedlist仅保证单个方法调用原子性,不保证复合操作、遍历及视图操作的线程安全;必须严格控制原始引用,遍历和复合操作需手动加锁,适用于低并发写稀疏场景。

Collections.synchronizedList 是 Java 中最轻量、最直接的线程安全 List 封装方式,但它不是“一包就安全”的黑盒——它的线程安全性有明确边界:只保单个方法调用原子,不保逻辑组合、不保遍历过程、不保访问路径隔离。
初始化必须封死原始引用
同步效果完全依赖于“唯一访问入口”。一旦原始 ArrayList 被其他变量持有并直接操作,所有同步立刻失效。
- ✅ 正确写法:private final List
safeList = Collections.synchronizedList(new ArrayList()); - ❌ 危险写法:List
raw = new ArrayList(); List —— 后续若有人用safe = Collections.synchronizedList(raw); raw.add(...),锁形同虚设 - ⚠️ 连带风险:对
safeList.subList(0, 5)或Arrays.asList(safeList)的调用,返回的都是非同步视图,不可用于并发场景
遍历必须整块加锁,不能只锁一次
它返回的 Iterator 或增强 for 循环底层仍是普通迭代器,不具备同步能力。不加锁遍历大概率触发 ConcurrentModificationException,或出现漏项、重复、甚至数据错乱。
- 正确做法:用
synchronized(safeList)包裹整个遍历逻辑,包括iterator()获取和所有next()调用 - 等价写法:也可用原始列表引用(如
synchronized(raw)),因二者共享同一锁对象(this) - 更稳妥实践:先获取快照再遍历,避免长临界区 ——
List<string> snapshot = new ArrayList(safeList);</string>,之后在同步块外遍历snapshot
复合操作必须手动包裹,不能拆开调用
“检查再执行”类逻辑天然非原子:size() == 0 和 add(x) 之间可能插入其他线程的修改;contains(x) 和 remove(x) 之间可能被抢先删除或添加。
- 典型错误:
if (!safeList.contains("a")) safeList.add("a");→ 可能重复添加 - 正确写法:
synchronized (safeList) { if (!safeList.contains("a")) safeList.add("a"); } - 注意:锁对象必须是
safeList(或其底层引用),不能用新创建的new Object(),否则锁不生效
选它还是换别的?看读写比例和一致性要求
它适合低并发、写操作稀疏、且能严格控制访问路径的场景;一旦写频升高或遍历密集,性能和可靠性会迅速下降。
- ✔️ 推荐用 synchronizedList:监听器注册/注销、配置初始化填充、状态标记汇总等“写极少、读偶发、需强实时性”的场景
- ✔️ 换 CopyOnWriteArrayList:当遍历极其频繁(如每秒数百次)、写操作极少(如分钟级配置刷新)、能接受写时复制开销和旧快照语义
- ⚠️ 明确不适用:高吞吐写场景(如高频事件缓冲)、多线程协同修改同一列表、需要 CAS 或条件更新逻辑
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











