【问题标题】:ConcurrentModificationException with ConcurrentHashMap带有 ConcurrentHashMap 的 ConcurrentModificationException
【发布时间】:2020-08-13 17:50:25
【问题描述】:

我理解它背后的概念,但认为使用 ConcurrentHashMap 而不是 HashMap 可以解决它。因为 ConcurrentHashMap 可以防止不同线程的并发读取和修改。

但我仍然看到异常。

这是代码sn-p-

SampleFile.java

prepareInfo(RequestHelper.getSender(request), someVar, concurrentMap);
....
...
    private void prepareInfo(final Sender sender, final SomeVar someVar, final ConcurrentHashMap<String,
            Object> concurrentMap){
        final Info info = RequestHelper.getInfo(someVar);
        someVar.setInfo(info);
        if(sender != null){
            prepareProfileInfo(sender.getUserDetails(), info, concurrentMap);
            mapDetailsWithMap(sender.getDetails(), concurrentMap);
            if(sender.getSenderId() != null){
                concurrentMap.put("sender_id", sender.getSenderId());
            }
            concurrentMap.putAll(sender.getAdditionalProperties());
        }
    }

错误堆栈跟踪是 -

    at java.util.HashMap$HashIterator.nextNode(HashMap.java:1445)
    at java.util.HashMap$EntryIterator.next(HashMap.java:1479)
    at java.util.HashMap$EntryIterator.next(HashMap.java:1477)
    at java.util.concurrent.ConcurrentHashMap.putAll(ConcurrentHashMap.java:1083)
    at SampleFile.prepareAccountInfo(SampleFile.java:114)

几个我不清楚的问题-

  1. 为什么异常仍然发生?
  2. 我们可以通过单元测试来测试修复吗?

【问题讨论】:

    标签: java collections iteration concurrenthashmap concurrentmodification


    【解决方案1】:

    这一行用sender.getAdditionalProperties() 检索一个HashMap,然后在HashMap 上进行迭代,将每个项目添加到concurrentMap

            concurrentMap.putAll(sender.getAdditionalProperties());
    

    如果在迭代运行时修改了sender 中的HashMap,您将收到 ConcurrentModificationException。异常的意思是“我在迭代时修改了地图的结构,所以我现在不知道该怎么办”。

    要允许在 sender 对象内的映射上同时修改和迭代,该映射应该是 ConcurrentHashMap

    要测试修复,您可以进行以下测试:

    Map<String,Object> map =  sender.getAdditionalProperties()
    map.put("foo", "bar");
    Iterator<Map.Entry<String, Object>> iterator = map.entrySet().iterator();
    // uh-oh - adding an item invalidates HashMap iterator
    map.put("bar", "baz");
    // Throws exception for HashMap
    iterator.next();
    

    【讨论】:

    • 那么我应该遍历sender.getAdditionalProperties()返回的地图吗?此外,那时不需要 ConcurrentHashMap - 只有当我迭代它时才会使用 HashMap 吗?
    • 如果修改 sender.additionalProperties 映射是一个有效用例,同时您还对其进行迭代(可能在单独的线程中),那么 sender.additionalProperties 应该是 ConcurrentHashMap
    • concurrentMap.putAll(sender.getAdditionalProperties());ConcurrentModificationException 失败时,修改必须在不同的线程中进行,因为putAll 在迭代时不会修改传入的地图。在这种情况下,在Sender 中使用线程安全映射只是一个修补程序。它不会改变有人正在修改您只是试图复制的地图的根本问题,因此生成的副本的内容完全是不确定的。
    • @Holger 想知道是因为 concurrentMap 被提前一步修改还是问题出在 sender.getAdditionalProperties 返回的地图上?我无法弄清楚 sender.getAdditionalProperties 是如何被修改的。
    • 如果sender.additionalProperties在构建后不应该被修改,你可以用additionalProperties = Collections.unmodifiableMap(additionalProperties)把它变成一个不可修改的地图如果它仍然被修改你会发现快出来
    【解决方案2】:

    您犯了一个可以理解且非常常见的错误。

    你看到ConcurrentModificationException,你想:嗯。显然,线程。

    但那是错误的。这个名字……也许是不幸的。

    ConcurrentModificationException 与线程无关

    CoModEx 仅仅意味着这一系列事件已经发生:

    1. 您已经从集合中创建了一个迭代器,例如致电.iterator(),或写for (var x : someCollection) 即可。
    2. 在创建迭代器后的某个时间点,您会以某种方式更改基础集合。你清除了它,添加了一些东西,更新了一个值,或者删除了一些东西。任何变化。
    3. 您可以对第 1 步中创建的迭代器执行任何操作,例如在其上调用 .hasNext()next(),或者您的 for (var x : someCollection) 到达右大括号/您运行 continue

    然后,繁荣。 CoModEx。

    这是一个简单的方法。注意这个应用程序是如何完全单线程的:

    class ThisAsplodes {
        public static void main(String[] args) {
            List<String> list = new ArrayList<String>();
            list.add("Okay");
            for (String elem : list) {
                list.add("Bye");
            }
        }
    }
    

    编译它,运行它,然后,瞧。并发修改异常。列表被修改(添加了“Bye”),然后在更改之前生成的迭代器上进行迭代(右大括号)。

    它变得更好:如果您尝试使用简单的 jane hashmap 和多个线程(一个线程创建迭代器,另一个线程修改它)来执行这一系列事件,那么 CoModEx 可能会发生,但更常见的是事情会变得很奇怪方法。例如,您刚刚添加的键似乎不在地图中。使用 ConcurrentHashMap,您可以获得可靠的 CoModEx。

    从你的代码中看不出你在哪里做这个,但至少现在你知道在哪里看。

    【讨论】:

      猜你喜欢
      • 2013-08-06
      • 1970-01-01
      • 2022-12-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-05
      • 2015-12-11
      相关资源
      最近更新 更多