如何调试ConcurrentModificationException?

时间:2009-05-08 14:49:30

标签: java multithreading exception concurrency

我遇到了ConcurrentModificationException,通过查看它我看不出它为什么会发生的原因;抛出异常的区域和修改集合的所有区域都被

包围
synchronized (this.locks.get(id)) {
  ...
} // locks is a HashMap<String, Object>;

我试图抓住那个讨厌的线程,但我所能指出的(通过在异常中设置断点)是抛出线程拥有监视器而另一个线程(程序中有两个线程)睡眠。


我该怎么办?当遇到类似的线程问题时,您通常会做什么?

6 个答案:

答案 0 :(得分:30)

它可能与同步块无关。当您在迭代其元素时修改集合时,经常会出现ConcurrentModificationException

List<String> messages = ...;
for (String message : messages) {
    // Prone to ConcurrentModificationException
    messages.add("A COMPLETELY NEW MESSAGE");
}

答案 1 :(得分:12)

与上一篇文章类似,如果删除条目,则可能会遇到同样的问题。 e.g。

for(String message : messages) {
  if (condition(message))
     messages.remove(message);
}

另一个常见的例子是清理地图。

可以使用Iterator明确解决此特定问题。

for(Iterator<String> iter = messages.iterator(); iter.hasNext();) {
   String message = iter.next();
   if (condition(message))
       iter.remove(); // doesn't cause a ConcurrentModificationException 
}

答案 2 :(得分:5)

有时您的应用程序可能过于复杂而某些功能可能会产生太多副作用。另外,也许另一个线程确实对该列表做错了,你无法轻易找到。

对于我自己的问题,我编写了自己的列表系统,委托另一个列表,一旦锁定,所有其他修改抛出ConcurrentModificationException,因此错误的修改指令将在输出处获得异常。它还可以检测上述错误。

import java.util.*;

/**
 * Created by IntelliJ IDEA.
 * User: francoiscassistat
 * Date: 12 juin 2010
 * Time: 18:20:18
 *
 *
 * Lockable list, made to debug ConcurrentModificationException on Lists.
 * The lock can be switched on/off with setLocked(boolean).
 * When locked, all write access to the list or iterators gets ConcurrentModificationException.
 * Simple usage case :
 *
 * list.setLocked(true);
 *
 * for (Object o : list.iterator()) // now this won't get ConcurrentModificationException, the other instruction that cause this will thrown the exception
 * { ... }
 *
 * list.setLocked(false);
 */
public class LockableList<E> implements List<E> {
    protected class LockableListIterator implements Iterator<E> {
        protected Iterator<E> iterator;

        public LockableListIterator(Iterator<E> iterator) {
            this.iterator = iterator;
        }

        public boolean hasNext() {
            return iterator.hasNext();
        }

        public E next() {
            return iterator.next();
        }

        public void remove() {
            checkLock();
            iterator.remove();
        }
    }

    protected class LockableListListIterator implements ListIterator<E> {
        protected ListIterator<E> listIterator;

        public LockableListListIterator(ListIterator<E> listIterator) {
            this.listIterator = listIterator;
        }

        public boolean hasNext() {
            return listIterator.hasNext();
        }

        public E next() {
            return listIterator.next();
        }

        public boolean hasPrevious() {
            return listIterator.hasPrevious();
        }

        public E previous() {
            return listIterator.previous();
        }

        public int nextIndex() {
            return listIterator.nextIndex();
        }

        public int previousIndex() {
            return listIterator.previousIndex();
        }

        public void remove() {
            checkLock();
            listIterator.remove();
        }

        public void set(E e) {
            checkLock();
            listIterator.set(e);
        }

        public void add(E e) {
            checkLock();
            listIterator.add(e);
        }
    }

    protected class LockableListSubList implements List<E>
    {
        protected List<E> list;

        public LockableListSubList(List<E> list) {
            this.list = list;
        }

        public int size() {
            return list.size();
        }

        public boolean isEmpty() {
            return list.isEmpty();
        }

        public boolean contains(Object o) {
            return list.contains(o);
        }

        public Iterator<E> iterator() {
            return new LockableListIterator(list.iterator());
        }

        public Object[] toArray() {
            return list.toArray();
        }

        public <T> T[] toArray(T[] a) {
            return list.toArray(a);
        }

        public boolean add(E e) {
            checkLock();
            return list.add(e);
        }

        public boolean remove(Object o) {
            checkLock();
            return list.remove(o);
        }

        public boolean containsAll(Collection<?> c) {
            return list.containsAll(c);
        }

        public boolean addAll(Collection<? extends E> c) {
            checkLock();
            return list.addAll(c);
        }

        public boolean addAll(int index, Collection<? extends E> c) {
            checkLock();
            return list.addAll(index, c);
        }

        public boolean removeAll(Collection<?> c) {
            checkLock();
            return list.removeAll(c);
        }

        public boolean retainAll(Collection<?> c) {
            checkLock();
            return list.retainAll(c);
        }

        public void clear() {
            checkLock();
            list.clear();
        }

        @Override
        public boolean equals(Object o) {
            return list.equals(o);
        }

        @Override
        public int hashCode() {
            return list.hashCode();
        }

        public E get(int index) {
            return list.get(index);
        }

        public E set(int index, E element) {
            checkLock();
            return list.set(index, element);
        }

        public void add(int index, E element) {
            checkLock();
            list.add(index, element);
        }

        public E remove(int index) {
            checkLock();
            return list.remove(index);
        }

        public int indexOf(Object o) {
            return list.indexOf(o);
        }

        public int lastIndexOf(Object o) {
            return list.lastIndexOf(o);
        }

        public ListIterator<E> listIterator() {
            return new LockableListListIterator(list.listIterator());
        }

        public ListIterator<E> listIterator(int index) {
            return new LockableListListIterator(list.listIterator(index));
        }

        public List<E> subList(int fromIndex, int toIndex) {
            return new LockableListSubList(list.subList(fromIndex, toIndex));
        }
    }

    protected List<E> list;
    protected boolean locked;

    public LockableList(List<E> list) {
        this.list = list;
        locked = false;
    }

    public boolean isLocked() {
        return locked;
    }

    public void setLocked(boolean locked) {
        this.locked = locked;
    }

    protected void checkLock() {
        if (locked)
            throw new ConcurrentModificationException("Locked");
    }

    public int size() {
        return list.size();
    }

    public boolean isEmpty() {
        return list.isEmpty();
    }

    public boolean contains(Object o) {
        return list.contains(o);
    }

    public Iterator<E> iterator() {
        return new LockableListIterator(list.iterator());
    }

    public Object[] toArray() {
        return list.toArray();
    }

    public <T> T[] toArray(T[] a) {
        return list.toArray(a);
    }

    public boolean add(E e) {
        checkLock();
        return list.add(e);
    }

    public boolean remove(Object o) {
        checkLock();
        return list.remove(o);
    }

    public boolean containsAll(Collection<?> c) {
        return list.containsAll(c);
    }

    public boolean addAll(Collection<? extends E> c) {
        checkLock();
        return list.addAll(c);
    }

    public boolean addAll(int index, Collection<? extends E> c) {
        checkLock();
        return list.addAll(index, c);
    }

    public boolean removeAll(Collection<?> c) {
        checkLock();
        return list.removeAll(c);
    }

    public boolean retainAll(Collection<?> c) {
        checkLock();
        return list.retainAll(c);
    }

    public void clear() {
        checkLock();
        list.clear();
    }

    @Override
    public boolean equals(Object o) {
        return list.equals(o);
    }

    @Override
    public int hashCode() {
        return list.hashCode();
    }

    public E get(int index) {
        return list.get(index);
    }

    public E set(int index, E element) {
        checkLock();
        return list.set(index, element);
    }

    public void add(int index, E element) {
        checkLock();
        list.add(index, element);
    }

    public E remove(int index) {
        checkLock();
        return list.remove(index);
    }

    public int indexOf(Object o) {
        return list.indexOf(o);
    }

    public int lastIndexOf(Object o) {
        return list.lastIndexOf(o);
    }

    public ListIterator<E> listIterator() {
        return new LockableListListIterator(list.listIterator());
    }

    public ListIterator<E> listIterator(int index) {
        return new LockableListListIterator(list.listIterator(index));
    }

    public List<E> subList(int fromIndex, int toIndex) {
        return new LockableListSubList(list.subList(fromIndex, toIndex));
    }
}

只需像这样使用它:

List list = new LockableList(new ArrayList(...));
list.setLocked(true);

for (E e : list.iterator())
{ ... }

list.setLocked(false);

希望它可以帮助别人。

答案 3 :(得分:4)

如果您需要从列表中删除少量元素。您可以维护另一个列表,例如要删除的元素。最后调用removeAll(collection)。当然,这对于大量数据来说并不好。

答案 4 :(得分:2)

由于不得不处理类似的问题,我编写了一个小助手来调试某些对象的并发访问情况(有时使用调试器会修改运行时行为以至于不会发生问题)。这种方法类似于弗朗索瓦所展示的方法,但更为通用。也许它有助于某人:http://code.google.com/p/kongcurrent/

答案 5 :(得分:1)

在迭代它时修改动态列表时会收到ConcurrentModificationException(例如,在foreach循环中)。您可能希望确保在任何地方都不这样做。