2
List<Ball> myObjs = myThreads[threadIndex].getMyObjList();
int initialSize = Collections.synchronizedList(myObjs).size();

引发 ConcurrentModificationException。我也试过把它放在一个 synchronized(myObjs) 块中,但它也没有用。解决办法是什么?在我使用此列表的其他任何地方,它都位于同步(块)中。

PS 这个错误最终也会产生 BrokenBarrierException。(是的,我正在使用循环屏障进行同步)

编辑:这是堆栈跟踪:

Exception in thread "Thread-3" java.util.ConcurrentModificationException
at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1091)
at java.util.ArrayList$SubList.size(ArrayList.java:921)
at java.util.Collections$SynchronizedCollection.size(Collections.java:1573)
at Part2.Animation.processCollisions(MyClass.java:133) // This is the call to size()

编辑:循环看起来像

for (int threadIndex=0; threadIndex < numThreads; threadIndex++) {
  List<Ball> myObjs = myThreads[threadIndex].getMyObjList();
  int initialSize = Collections.synchronizedList(myObjs).size();
}

并且无论 numThreads 是多少,当 threadIndex=1 时都会发生异常。

4

3 回答 3

4

查看某些List类的源代码,我看不出调用如何可能size()会抛出ConcurrentModificationException. 如果您向我们展示堆栈跟踪信息,将会有所帮助。


但与此同时,你似乎对做什么有一个基本的误解Collections.synchronizedList。它所做的是创建并返回一个列表包装器,以确保对同一包装器实例的操作是同步的。

它无论如何都不会阻止线程在底层 list 上执行不同步的操作;即您刚刚包装的列表对象。如果一个线程具有底层列表的引用,它可以直接访问它而无需通过包装器。它也没有做任何事情来同步使用不同包装器对同一底层列表的操作。因此,如果您Collections.synchronizedList在同一个列表上调用两次,您将创建两个彼此不同步的不同包装器。

因此,实际上您Collections.synchronizedList(myObjs).size()根本没有执行任何有意义的同步。没有其他线程可以获取创建的同步列表包装器,因此没有其他线程可以通过包装器与此同步。

于 2012-07-23T03:34:33.943 回答
2

它是由 引起的subList(),与同步无关。

myObjs是 subList() 的结果,实际上 myObjs 是原始 List 的视图。如果原来的 List 被修改,ConcurrentModificationException会在subList.size()被调用时发生。这是有道理的,视图应该与其原始列表一致。

相反,您可以获得如下子列表:

List mySubList = new ArrayList(originalList.subList(a, b));
于 2012-07-25T05:09:07.927 回答
0

CopyOnWriteArrayList如果要修改具有 2 个或以上线程的列表,则应使用。

“这个数组在迭代器的生命周期内永远不会改变,所以干扰是不可能的,并且保证迭代器不会抛出 ConcurrentModificationException。”

于 2012-07-23T03:26:51.630 回答