ConcurrentModificationError в stati c синхронизированном методе - PullRequest
0 голосов
/ 17 марта 2020

У меня есть класс, который содержит поле и два static synchronized метода. Поле является картой, ключ которой является целым числом, а значение - списком. Один из методов используется для добавления нового элемента в список, а другой - для чтения всех элементов в списке. У меня есть несколько тем для чтения и записи map. Я исключаю некоторые детали в коде, поэтому давайте предположим, что ключ всегда находится на карте.

Насколько я понимаю, только один поток может вводить либо write, либо read одновременно, поскольку оба они объявлен как static synchronized. Это означает, что когда один поток что-то пишет, другие потоки не могут писать или читать, и наоборот. Но почему итератор итерирует в ConcurrentModificationError значение, возвращаемое map.get(i) в строке for(int item: map.get(i))? Кто-нибудь может объяснить, в чем причина? Заранее спасибо!

class A {
   private static Map<Integer, List<Integer>> map;

   public static synchronized void write(int i, int item) {
      map.get(i).add(item);
   }

   public static synchronized void read(int i) {
      for(int item: map.get(i)) System.out.println(item);
   }
}

1 Ответ

1 голос
/ 17 марта 2020

Я думаю, что где-то в вашем коде есть одновременная модификация списка, содержащегося на карте. Я написал небольшой код, который выдает то же исключение ConcurrentModificationException:

public class A {
    private static Map<Integer, List<Integer>> map = new HashMap<>();

    public static synchronized void write(int i, int item) {
        if (map.containsKey(i))
            map.get(i).add(item);
        else {
            ArrayList<Integer> list = new ArrayList<>();
            list.add(item);
            map.put(i, list);
        }
    }

    public static synchronized void read(int i) {
        for (int item : map.get(i))
            System.out.println(item);
    }

    public static void main(String[] args) {
        Runnable writeAction = () -> A.write(1, 1);
        Runnable readAction = () -> A.read(1);
        Runnable modifyAction = () -> A.modifyList(1, 2);

        ExecutorService service = Executors.newFixedThreadPool(4);
        for (int i = 0; i < 1_000; i++) {
            service.execute(writeAction);
            service.execute(readAction);
            service.execute(modifyAction);
        }

        service.shutdown();
    }

    /**
     * not synchronized
     * @param i
     * @param item
     */
    public static void modifyList(int i, int item) {
        if (map.containsKey(i))
            map.get(i).add(item);
        else {
            ArrayList<Integer> list = new ArrayList<>();
            list.add(item);
            map.put(i, list);
        }
    }

}

Этот код определенно генерирует исключение, когда два потока в пуле одновременно выполняют метод modifyList () и read ().

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

...