Java - 在List上同步getSize()

时间:2012-07-23 02:58:34

标签: java multithreading synchronization

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

抛出ConcurrentModificationException。我也尝试过这个 synchronized(myObjs)块,但它也没有用。解决办法是什么?在我使用此列表的其他任何地方,它都在同步(块)中。

P.S。此错误也最终导致生成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时都会发生异常。

3 个答案:

答案 0 :(得分:3)

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


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

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

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

答案 1 :(得分:2)

subList()引起,与同步无关。

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

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

List mySubList = new ArrayList(originalList.subList(a, b));

答案 2 :(得分:0)

如果要修改包含2个或更多线程的列表,则应使用CopyOnWriteArrayList

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