Как отлаживать ConcurrentModificationException?

Я столкнулся с ConcurrentModificationException и посмотрев на него, я не вижу причины, почему это происходит; область, отбрасывающая исключение, и все места, изменяющие коллекцию, окружены

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

Я попытался поймать жуткий поток, но все, что я мог прибить (установив точку останова в исключении), - это то, что бросающий поток владеет монитором, в то время как другой поток (есть два потока в программе) спит.


Как мне продолжить? Что вы обычно делаете, когда сталкиваетесь с подобными проблемами с потоками?

Ответ 1

Это может не иметь никакого отношения к блоку синхронизации. ConcurrentModificationException часто возникает, когда вы изменяете коллекцию во время итерации по ее элементам.

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

Ответ 2

Как и в предыдущем посте, вы можете получить ту же проблему, если вы удалите запись. например.

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 
}

Ответ 3

Иногда ваше приложение может быть слишком сложным, и некоторые функции могут иметь слишком большой побочный эффект. Кроме того, возможно, другой поток действительно делает что-то не так с этим списком, и вы не можете найти, где легко.

Для моей собственной проблемы я написал свою собственную систему списков, которая делегирует другой список, и после блокировки все другие модификации вызывают 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);

Надеюсь, что это может помочь кому-то другому.

Ответ 4

если вам нужно удалить несколько элементов из списка. Вы можете сохранить другой список, например, элементы, которые нужно удалить. И, наконец, вызовите removeAll (коллекция). Конечно, это не хорошо для огромных данных.

Ответ 5

Чтобы справиться с подобными проблемами, я написал небольшой помощник для отладки параллельных ситуаций доступа к определенным объектам (иногда использование отладчика так сильно изменяет поведение во время выполнения, что проблема не возникает). Этот подход похож на тот, который показал Франсуа, но немного более общий. Возможно, это помогает кому-то: http://code.google.com/p/kongcurrent/

Ответ 6

Общепринято получать исключение ConcurrentModificationException при изменении динамического списка при итерации по нему (например, в foreach-loop). Вы можете захотеть убедиться, что вы этого не делаете.