collections.synchronizedlist不能解决并发修改问题,因其仅同步单个方法,不保证复合操作原子性;遍历时修改仍抛concurrentmodificationexception,需手动用synchronized(list)块保护所有复合操作。

为什么 Collections.synchronizedList 不能直接解决并发修改问题
它只是给每个方法加了 synchronized,但不保证复合操作原子性。比如遍历 + 修改的组合操作,仍会抛 ConcurrentModificationException 或读到脏数据。
常见错误现象:
- 用
for-each遍历同步列表时,在循环中调用remove()—— 依然报错 - 判断存在再添加(
if (!list.contains(x)) list.add(x))—— 不是原子操作,可能重复插入 - 多线程下看似“安全”,实则逻辑出错,且难以复现
正确使用 Collections.synchronizedList 的前提条件
必须手动对所有复合操作加锁,且锁对象必须是列表本身的 mutex(即 list 的 synchronized 监视器)。
实操建议:
- 获取原始包装后的列表后,**所有外部访问都必须通过
synchronized (list) { ... }块**,不能依赖单个方法的同步 - 不要用
list.iterator()遍历;改用传统for (int i = 0; i ,并在同步块内完成 - 如果需要频繁复合操作,不如直接换用
CopyOnWriteArrayList(适合读多写少)或ConcurrentLinkedQueue(无界、弱一致性)
示例:安全的“检查后添加”
synchronized (list) {
if (!list.contains(item)) {
list.add(item);
}
}
Collections.synchronizedList 和 CopyOnWriteArrayList 性能差异在哪
前者是“方法级同步”,读写都阻塞;后者读不加锁、写时复制整个数组,适合读远多于写的场景。
关键区别:
-
Collections.synchronizedList:写操作快(O(1)),但高并发读写时争用严重,吞吐量下降明显 -
CopyOnWriteArrayList:读操作无锁、极快;但每次写都复制数组,内存开销大,且迭代器看到的是快照,无法反映后续修改 - 若列表长度常超千项、写操作频繁(如每秒几十次以上),
CopyOnWriteArrayList可能引发 GC 压力或延迟突增
容易被忽略的初始化陷阱
传入 Collections.synchronizedList 的原始列表不能是不可变或懒加载集合(如 Arrays.asList() 返回的固定大小列表),否则运行时可能抛 UnsupportedOperationException。
务必确认底层实现支持动态增删:
- ✅ 推荐:
new ArrayList()、new LinkedList() - ❌ 避免:
Arrays.asList(...)、Collections.unmodifiableList(...)、Hibernate 的代理集合 - ⚠️ 注意:如果原始列表本身已被其他线程共享且未同步,包装后也无法消除已有竞态
正确初始化写法:
List<string> list = Collections.synchronizedList(new ArrayList());</string>
一旦用了 Collections.synchronizedList,就别再把内部原始列表暴露出去 —— 同步契约只对包装后的引用有效。










