LinkedList 中的 ConcurrentModificationException

5
我正在尝试设计一个软件,可以将流程图转换为Java或其他代码。然而,我一直遇到ConcurrentModificationException错误。但是我不能使用布尔值来防止并发修改,因为对链表的访问发生在不同的位置。
所以,作为解决方案,我创建了下面的适配器类。然而,它也会从next方法抛出相同的异常。是否有其他解决方案,如果可以,请告诉我如何修改我的代码...
非常感谢您的帮助。
import java.util.Iterator;
import java.util.LinkedList;

public class LinkedListAdapter<T> extends LinkedList<T>{

@Override
public boolean add(T t){

    boolean b;

    synchronized(this){
        b = super.add(t);
    }

    return b;
}

@Override
public T remove(){

    T t;

    synchronized(this){
        t = super.remove();
    }

    return t;
}

@Override
public Iterator<T> iterator(){

    final LinkedListAdapter<T> adap = this;

    return 
        new Iterator<T>(){

        private Iterator<T> iter;

        {
            synchronized(adap){
                iter = LinkedListAdapter.this.getIterator();
            }
        }

        @Override
        public boolean hasNext() {

            boolean b;

            synchronized(adap){
                b = iter.hasNext();
            }

            return b;
        }

        @Override
        public T next() {

            T t;

            synchronized(adap){
                t = iter.next();
            }

            return t;
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    };
}

protected Iterator<T> getIterator() {

    Iterator<T> iter;

    synchronized(this){
        iter = super.iterator();
    }

    return iter;
}
}

3
“@SLaks的评论‘一旦你修复它,它就能正常工作’可能是SO上最有帮助的评论。” - josefx
1
@josefx:我的观点是他的问题完全颠倒了处理方式。 - SLaks
是的,没错。在设计软件时,我没有预料到任何多线程问题,因为我使用了单个线程。然而,在使用paintComponent时,多线程问题出现得出乎意料。作为解决方案,我尝试使用适配器。非常感谢。 - Sameera Kumarasingha
7个回答

12

ConcurrentModificationException通常在遍历列表时抛出,在同一时间内通常另一个线程甚至是同一循环尝试修改(添加/删除)列表内容。


是的,在我的程序中有两个线程同时迭代,但是它们的编码在不同的位置。因此我尝试制作一个适配器。非常感谢。 - Sameera Kumarasingha
我该如何在列表中搜索要删除的项目并将它们删除呢? - Aaron Franke

4
使用synchronizedList或synchronized list时,在迭代时仍需要在外部进行同步。如果使用ConcurrentLinkedQueue,则不会出现这些问题。
Queue<Task> tasks = new ConcurrentLinkedQueue<Task>();
tasks.add(task); // thread safe
tasks.remove(task2); // thread safe

for(Task t: tasks) // can iterate without a CME.

注意:如果您正在使用另一个线程的队列,我建议您使用 ExecutorService,因为它将队列与线程池相结合,使得处理“后台”线程变得更加容易。

3

1

当您在循环体中迭代列表并向其中添加元素时,就会出现这种情况。您可以使用迭代器的remove()方法安全地删除元素,但不能通过调用列表本身的任何remove()方法来删除元素。

解决方案是在迭代列表之前复制该列表:

List<T> copy = new ArrayList<T>( list );
for( T e : copy ) {
    ... you can now modify "list" safely ...
}

这是最干净的解决方案。 - jfajunior

1
Java集合是“快速失败”的,这意味着一旦底层的集合被修改,所有现有的迭代器都会变得无效——同步修改并不能阻止列表使所有迭代器失效。
作为解决办法,您可以创建一个列表副本进行迭代,或者推迟修改直到迭代完成。要删除条目,您还可以使用iterator.remove()方法,该方法使迭代器本身有效。

0

这里的答案:为什么会出现java.util.ConcurrentModificationException? 对我帮助很大。

我将在此复制粘贴它,以防有人想要修复此错误:

当您遍历列表时,不能从中删除项目。这样做会导致异常。

请执行以下操作:

int size = list.size();
for (int i = 0 ; i< size ; i++) {
   list.add(0,"art");
   list.remove(6);
   System.out.println(list);
}

0
List<X> myList = ....
List<X> myThreadSafeList = synchronizedList(myList);

synchronizedList(myList)}

注意JavaDoc中的以下语句:

在迭代返回列表时,用户必须手动对其进行同步:

List list = Collections.synchronizedList(new ArrayList());
    ...
synchronized(list) {
    Iterator i = list.iterator(); // Must be in synchronized block
  while (i.hasNext())
      foo(i.next());
}

很遗憾这个不起作用。我尝试在两个定时器中都使用synchronized(list)来修改列表。 - Colateral

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接