有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

多线程Java同步列表上的getSize()

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

抛出ConcurrentModificationException。我也试着把这个放在一个盒子里 同步(myObjs)块,但也不起作用。解决办法是什么?在我使用此列表的其他地方,它都在一个同步(块)中

另外,此错误最终也会产生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) 个答案

  1. # 1 楼答案

    查看一些List类的源代码,我看不出调用size()将抛出ConcurrentModificationException的可能性。如果你给我们看一个堆栈跟踪会有帮助的


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

    无论如何,它不会阻止线程对底层列表执行非同步操作;i、 e.刚刚包装的列表对象。如果一个线程有底层列表的引用,那么它可以直接访问它,而无需经过包装器。对于同一个基础列表,它也不能使用不同的包装器来同步操作。因此,如果在同一个列表中调用Collections.synchronizedList两次,将创建两个不同的包装器,它们彼此不同步

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

  2. # 2 楼答案

    如果要修改包含2个或以上线程的列表,应该使用CopyOnWriteArrayList

    “该数组在迭代器的生命周期内不会更改,因此不可能发生干扰,并且迭代器保证不会抛出ConcurrentModificationException。”

  3. # 3 楼答案

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

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

    相反,你可以得到如下子列表:

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