静态同步方法中的ConcurrentModificationError

问题描述 投票:0回答:1

我有一个包含一个字段和两个static synchronized方法的类。该字段是一个映射,其键是整数,值是列表。一种方法用于在列表中添加新项目,另一种方法用于读取列表中的所有项目。我有多个线程可以读取和写入map。我消除了代码中的一些细节,因此让我们假设密钥始终在地图中。

据我所知,只有一个线程可以同时输入writeread,因为它们都被声明为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);
   }
}
java multithreading concurrency concurrentmodification
1个回答
1
投票

我认为,在代码中的某个地方,同时保存了地图所保存的列表。我写了一个小的代码,引发了相同的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()方法时,此代码肯定会引发异常。

因此,您的类中有一个不同步的方法,或者某个吸气剂返回了原始列表的引用,而不是它的副本(我的意思是转义引用),无论返回到哪个方法,他们都在修改列出并发。

© www.soinside.com 2019 - 2024. All rights reserved.