Collections.synchronizedList仅保证单个操作原子性,复合操作需手动同步底层列表;迭代必须synchronized(list),不可用synchronized(syncList);相比CopyOnWriteArrayList,它适合读多写少且需随机修改的场景。

直接用 Collections.synchronizedList 包装普通 ArrayList 或其他 List 实现,能获得一个线程安全的列表对象,但要注意它只保证单个操作的原子性,复合操作仍需手动同步。
基本用法:创建同步包装列表
传入一个非空的原始 List,返回一个线程安全的代理对象:
List<String> list = new ArrayList<>(); List<String> syncList = Collections.synchronizedList(list);
此后所有通过 syncList 调用的方法(如 add()、get()、size())都自动加锁,内部使用该列表对象自身的锁(即 list 实例的 intrinsic lock)。
必须手动同步的迭代和复合操作
synchronizedList 不保护遍历过程。以下代码是线程不安全的:
立即学习“Java免费学习笔记(深入)”;
// ❌ 危险:迭代期间可能被其他线程修改
for (String s : syncList) {
System.out.println(s);
}
正确做法是显式同步在原始列表上(不是包装对象):
// ✅ 安全:同步在底层 list 上
synchronized (list) {
for (String s : syncList) {
System.out.println(s);
}
}
类似地,像“检查是否存在再添加”这类判断+修改组合操作,也需外部同步:
-
if (!syncList.contains(x)) syncList.add(x);→ 非原子,需包裹在synchronized(list)块中 -
syncList.get(0)和syncList.set(0, x)单独调用是安全的,但连用时若依赖中间状态,也要同步
与 CopyOnWriteArrayList 的对比选择
synchronizedList 适合读多写少、且需要支持随机修改(如 set(index, e))、或已有大量遗留代码依赖 List 接口的场景;而 CopyOnWriteArrayList 更适合读极多、写极少、且迭代频繁的场景(如监听器列表),它的迭代器不抛 ConcurrentModificationException,也不需额外同步。
注意:CopyOnWriteArrayList 写操作开销大(每次复制整个数组),不适合高频更新。
注意事项和常见误区
- 不要用
synchronized(syncList)—— 锁对象是底层原始列表,不是包装对象 - 原始列表(如
list)不应再被其他线程直接访问,否则破坏同步契约 - 返回的包装对象仍是
List类型,可直接用于原有接口,无需改类型声明 - 它不解决结构性修改(如
clear())与其他操作的竞态,仍要按需同步


















