collections.synchronizedlist仅保证单个操作原子性,复合操作如遍历+修改仍线程不安全,需手动synchronized块包裹迭代;必须用final封装、显式泛型、避免暴露原始引用。

为什么 Collections.synchronizedList 返回的集合仍可能出错
它只保证单个操作(如 add、get)是原子的,不保证复合操作线程安全。比如遍历 + 修改组合逻辑,仍会抛 ConcurrentModificationException 或读到脏数据。
常见错误现象:for (Object o : list) { ... } 在多线程下并发修改时崩溃;或迭代中调用 removeIf 导致行为不可预测。
- 所有迭代必须手动加锁:用
synchronized (list) { ... }包裹整个遍历块 -
list.iterator()返回的迭代器本身不是同步的,不能直接在多线程里共享使用 - 如果底层传入的是未同步的
ArrayList,包装后不会改变其内部状态可见性,仅靠方法入口加锁
怎么正确初始化并使用 Collections.synchronizedList
必须确保被包装的原始列表本身不被其他代码绕过同步直接访问,否则锁失效。
推荐写法:private final List<string> list = Collections.synchronizedList(new ArrayList());</string>,且该变量设为 final 并不暴露原始引用。
- 不要写成
List<string> raw = new ArrayList(); List<string> sync = Collections.synchronizedList(raw);</string></string>,然后 elsewhere 还在用raw - 避免在构造后把同步列表再转成
Arrays.asList或通过subList暴露非同步视图 - 若需默认容量,先建带初始容量的
ArrayList再包装,如new ArrayList(16)
Collections.synchronizedList 和 CopyOnWriteArrayList 怎么选
前者适合读多写极少、且能接受手动同步迭代的场景;后者适合遍历远多于修改、不允许阻塞读线程的场景。
性能差异明显:Collections.synchronizedList 读写都阻塞,吞吐量低;CopyOnWriteArrayList 读无锁、写复制数组,内存开销大、迭代器不会抛并发异常但看不到最新写入。
- 高频遍历 + 偶尔增删 → 优先
CopyOnWriteArrayList - 写操作较频繁(如每秒几十次以上)+ 需强一致性 → 考虑
ConcurrentLinkedQueue或分段锁结构 - 已有代码大量依赖
List接口且无法改逻辑 →Collections.synchronizedList是最小侵入方案,但必须补全同步块
容易被忽略的坑:静态工具类返回值和泛型擦除
Collections.synchronizedList 是静态工厂方法,返回的是 List 接口实现,类型擦除后无法在运行时校验泛型安全性。若混用原始类型和参数化类型,编译期不报错,运行时可能 ClassCastException。
典型问题:把 Collections.synchronizedList(new ArrayList())(原始类型)赋给 List<string></string> 变量,后续插入 Integer 不报错,取出来强转就崩。
- 永远显式指定泛型:用
new ArrayList<string>()</string>或new ArrayList()(Java 7+) - 不要把同步列表作为 public static final 字段直接暴露,应封装在工具方法或服务类中控制访问路径
- 单元测试里务必覆盖多线程遍历 + 修改交叉场景,仅测单线程 add/get 没意义
synchronizedList 就以为万事大吉,结果卡在迭代上——那地方得你自己加 synchronized 块。










