Java: почему возникает ConcurrentModificationException с синхронизированным списком?

1

С помощью этого кода:

public class SynchroApp {

    public static void main(String[] args) {

        final List<String> unsyList = new ArrayList<>();
        final List<String> syList = Collections.synchronizedList(unsyList);

        TimerTask changeList = new TimerTask() {
            boolean addElem = false;

            @Override
            public void run() {
                // add / remove elements to keep size between 2 and 9
                if (syList.size() < 2)
                    addElem = true;
                else if (syList.size() > 8)
                    addElem = false;
                if (addElem)
                    syList.add(String.valueOf(System.currentTimeMillis()));
                else
                    syList.remove(0);
            }
        };

        TimerTask reverseList = new TimerTask() {
            @Override
            public void run() {
                try {
                    for (String s : syList)
                        s = new StringBuffer(s).reverse().toString();
                } catch (Exception e) {
                    System.out.println("Exception: " + e);
                }
            }
        };

        new Timer().scheduleAtFixedRate(changeList, 0L, 30L);
        new Timer().scheduleAtFixedRate(reverseList, 0L, 20L);
    }
}

Почему я все еще получаю какое-то ConcurrentModificationException на Iterator.next?

EDIT: обновление элементов списка в reverseList не работает (как поясняется в комментариях). Этот код должен работать как ожидалось:

for (int i = 0; i < syList.size(); i++)
    syList.set(i, new StringBuffer(syList.get(i)).reverse().toString());
  • 0
    Я не вижу next звонка в том коде, который вы опубликовали. Обратите внимание, однако, что цикл при run reverseList в данный момент ничего не делает (строки Java являются неизменяемыми).
  • 2
    Вы не можете использовать итератор и одновременно изменять содержимое списка. Итератор поймет, что в список были внесены изменения, и выдаст это исключение.
Показать ещё 8 комментариев
Теги:
arraylist
iterator
concurrentmodification

3 ответа

1
Лучший ответ

Даже большинство синхронизированных коллекций не любят модификации и итератор вместе. Из описания API Collections.synchronizedList:

Крайне важно, чтобы пользователь вручную синхронизировался по возвращенному списку при его итерации по нему:

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

Также: вместо этого вы можете использовать коллекции из java.concurrent. У них обычно есть более четкий подход к синхронизации.

  • 0
    Спасибо за ссылку на документацию по API.
3

Потому что вы изменяете список, идя на него.

Обратите внимание, что синхронизированный список только делает каждый из своих методов и методы его итератора синхронизированными. Итерация по синхронизированному списку по-прежнему является неатомной операцией, которая включает несколько вызовов синхронизированных методов. Если вы хотите сделать весь итерационный атом, вы должны его явно синхронизировать, используя список как блокировку:

synchronized (syList) {
    for (String s : syList) {
        s = new StringBuffer(s).reverse().toString();
    }
}
0

Итератор и модификатор должны использовать synchronizedList(), чтобы иметь возможность блокировки/синхронизации на объекте.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню