Java中Collections同步List仍抛出ConcurrentModificationException问题排查
Collections.synchronizedList转同步List后仍抛ConcurrentModificationException的原因及解决
问题场景
将普通ArrayList通过Collections.synchronizedList转为同步List后,多线程下执行迭代+修改操作,依然抛出ConcurrentModificationException异常,代码如下:
package Concurrent_Collections; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class DemoForConcurrentModificationException extends Thread { static List<Integer> myList = new ArrayList<Integer>(); static List<Integer> synchronizedList; public static void main(String[] args) { System.out.println("Main Thread"); synchronizedList = Collections.synchronizedList(myList); for (int i = 0; i < 10; i++) { synchronizedList.add(i); } DemoForConcurrentModificationException t = new DemoForConcurrentModificationException(); t.start(); for (Integer k : synchronizedList) { System.out.println("Main"); System.out.println(k); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } public void run() { System.out.println("Child Thread"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronizedList.add(100); } }
原因分析
Collections.synchronizedList仅为List的单个方法调用添加同步锁,保证单个add、get等操作的线程安全性,但迭代操作(比如增强for循环)属于复合操作:它会先获取迭代器,之后循环执行hasNext()和next(),这些步骤之间没有被整体加锁。
在你的代码中:
- 主线程用增强for循环遍历List,每次迭代间隔1秒
- 子线程在2秒后调用
add()修改List结构 - 主线程的迭代器会检测到List的modCount(修改次数)和迭代器自身的expectedModCount不一致,触发快速失败机制,抛出
ConcurrentModificationException
解决办法
办法一:迭代时手动加锁
synchronizedList本身可作为锁对象,在迭代的整个过程中加锁,保证迭代和其他修改操作互斥:
修改主线程的遍历代码:
synchronized(synchronizedList) { for (Integer k : synchronizedList) { System.out.println("Main"); System.out.println(k); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }
办法二:使用CopyOnWriteArrayList
CopyOnWriteArrayList是Java并发包专为并发迭代设计的List实现,它的修改操作(add、remove等)会复制一份新的底层数组,迭代操作基于旧数组的快照进行,不会受后续修改影响,自然不会抛出ConcurrentModificationException。
替换原有的同步List定义:
// 直接替换成CopyOnWriteArrayList,无需用Collections.synchronizedList包装 static List<Integer> synchronizedList = new CopyOnWriteArrayList<>();
内容的提问来源于stack exchange,提问作者Nitesh Agrawal
相关产品推荐
相关产品推荐

