Collections.synchronizedList()仅保证单个方法线程安全,遍历时需手动同步锁住list本身,否则仍可能抛ConcurrentModificationException或IndexOutOfBoundsException。

用 Collections.synchronizedList() 包装 ArrayList,能保证单个方法调用的线程安全,但不能自动保护遍历操作——这是最容易踩坑的地方。
为什么 synchronizedList 不是“全自动安全”
它只是给每个 public 方法(如 add()、get()、remove())加了 synchronized 锁,让这些操作原子化。但像下面这种“先检查再操作”的逻辑,依然会出问题:
- 调用
size()判断是否为空 - 接着调用
get(0)取第一个元素 - 两个动作之间可能有其他线程删掉了元素,导致
IndexOutOfBoundsException
更典型的是增强 for 循环遍历 + 同时写入:哪怕底层是 synchronizedList,只要在 for (x : list) 过程中另一个线程调用了 add(),仍会抛 ConcurrentModificationException。因为迭代器内部依赖 modCount 检查,而 synchronizedList 并不锁住整个遍历过程。
遍历时必须手动同步
要安全遍历,得显式用同一把锁包裹迭代逻辑。推荐写法:
List<string> syncList = Collections.synchronizedList(new ArrayList());
// ... 添加元素
// 安全遍历方式:synchronized 块锁定 list 本身
synchronized (syncList) {
Iterator<string> it = syncList.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
</string></string>
注意:不能只锁自己的对象或新建 Object 锁,必须锁住 syncList 实例本身,否则和 synchronizedList 内部用的不是同一把锁,起不到协同作用。
适用场景与替代建议
Collections.synchronizedList() 适合读多写少、且遍历逻辑简单、能统一加锁的场景。但如果:
- 频繁遍历 + 偶尔写入 → 更推荐
CopyOnWriteArrayList,它迭代时不加锁,也不抛 CME - 写操作密集(比如每秒上千次 add)→ 同步锁竞争严重,考虑分段锁结构或业务层队列缓冲
- 需要复合操作原子性(如“若不存在则添加”)→ 单靠 synchronizedList 不够,得升级为
ReentrantLock手动控制,或换用ConcurrentHashMap配合辅助逻辑
Vector 虽然也线程安全,但性能更差(所有方法粗粒度 synchronized),已不推荐新项目使用。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











