【问题标题】:Does Collections.synchronized map makes Iterator threadsafeCollections.synchronized 映射是否使迭代器线程安全
【发布时间】:2016-01-16 09:19:18
【问题描述】:

系统中有两个线程。一个是读取线程,另一个是写入线程。

使用以下代码同步地图。

Map<String,ArrayList<String>> m = Collections.synchronizedMap(new HashMap<String,ArrayList<String>())

读取线程获取映射值的迭代器,同时写入线程修改映射。

那么,我的问题是迭代器会抛出ConcurrentModificationException吗?

【问题讨论】:

  • 如何修改地图?您没有保留对它的引用。
  • 嗯,我要设计一个系统。我正在考虑使用 ReadWriteLock。这就是为什么我想探索所有可能的场景。
  • 您问题中的代码是 100% 线程安全的,因为无法修改地图 - 没有直接引用地图。
  • @Bohemian,同步视图不是不可变的。只需调用m.put(String, List); 即可修改底层地图

标签: java dictionary iterator synchronized concurrentmodification


【解决方案1】:

也许吧。这样做是不安全的。 documentation

当迭代任何集合视图时,用户必须在返回的地图上手动同步

Collections.synchronized... 使单个方法调用原子化,因此它们不需要进一步同步。但是迭代不仅仅是一个单一的方法调用,所以它需要额外的同步。下面是一个例子

    Map<String, String> shared = Collections.synchronizedMap(new HashMap<>());

    new Thread(() -> {
        while (true) {
            synchronized (shared) {
                for (String key : shared.keySet()) {
                    System.out.println(key);
                }
            }
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                break;
            }
        }
    }).start();

    new Thread(() -> {
        while (true) {
            try {
                // this is atomic
                shared.put(UUID.randomUUID().toString(), "Yo!");
                Thread.sleep(1000);
            } catch (Exception e) {
                break;
            }
        }
    }).start();
}

【讨论】:

  • 所以,我想我需要使用同步来读写,对吧?
  • @Touchstone 取决于您在写作期间所做的工作。单个方法调用否,如果需要多个原子是,请参阅答案中的编辑
【解决方案2】:

是的,Iterator 可能仍会抛出 ConcurrentModificationException,因为它与同步无关(尽管它的名字暗示了)。Iterator 尝试尽最大努力检测结构修改(添加或删除对象)尝试并且与 List 的操作是否同步无关。 一旦通过List.iterator() or List.listIterator() 获得迭代器,对列表所做的任何更改(除了迭代器本身)都将尽最大努力引发 CME 异常。 确保不抛出 ConcurrentModificationException 的唯一方法 要么先完成阅读器操作,然后完成写入器操作(反之亦然),要么使用来自ConcurrentHashMap的故障安全迭代器

Map hashmap = new HashMap<String,ArrayList<String>();
----

Map<String,ArrayList<String>> m = new ConcurrentHashMap<String,ArrayList<String>(hashmap));

ConcurrentHashMap 是一个故障安全迭代器,现在您可以专注于同步读写器操作,而不是担心 ConcurrentModificationException。

【讨论】:

  • 所以,我想我需要使用同步来读写,对吧?
  • 只有同步是不够的,你需要确保迭代器的操作不会相互交错。或者我建议你使用来自 ConcurrentHashMap 的故障安全迭代器
  • 但是故障安全迭代器使用深拷贝,会导致脏读!!
  • @Touchstone 那么我建议你应该同步你的迭代操作,从获取它 Map.values().iterator() 到使用该迭代器
  • 请注意,问题需要澄清 - 问题中的代码无法修改,因为没有对其进行引用。
猜你喜欢
  • 2023-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多