繁体   English   中英

将 Future 的结果组合到 HashMap 时出现 java.util.ConcurrentModificationException

[英]java.util.ConcurrentModificationException when combing results from Future's into HashMap

我遇到了一个问题,我在合并期货结果时间歇性地收到java.util.ConcurrentModificationException错误。 我已经尝试使我所有的 HashMaps 并发并且我尝试使用迭代器但错误仍然存​​在,我完全不知道这个错误是如何发生的。 我也没有遍历我插入的同一个 HashMap (就像在类似问题中所做的那样)。

对于我的代码,首先我构建了一堆返回 HashMap<String, HashSet> 的任务,然后使用 .invokeAll 我的任务都返回上面的 HashMap,然后我尝试将其合并在一起(我正在读取一个大的 CSV 到获取每列的所有唯一结果)。

我首先定义所有键。

HashMap<String, HashSet<String>> headersHashset = new HashMap<String, HashSet<String>>();
headers = Arrays.asList(br.readLine().split(delimiter));
for (String headerID : headers) {
    headersHashset.put(headerID, new HashSet<>());
}

然后我做我的任务,克隆密钥,进行处理并返回结果

tasks.add(() -> {
    HashMap<String, HashSet<String>> localHeadersHashset = (HashMap<String, HashSet<String>>) headersHashset.clone();
    
    for (String[] values : sampleSet.values()) {  // sampleSet is a SortedMap<Integer, String[]>
        int headerAsINT = 0;
        for (String value : values) {
            localHeadersHashset.get(headers.get(headerAsINT)).add(value);
            headerAsINT++;
        }
    }
        
    return localHeadersHashset;
});

我调用所有的任务,其中的结果被放入一个未来的列表中

ExecutorService es = Executors.newCachedThreadPool();
List<Future<HashMap<String, HashSet<String>>>> futures = null;
try {
    futures = es.invokeAll(tasks);
    es.shutdown();
} catch (InterruptedException e) {
    e.printStackTrace();
}

然后,为了非常安全,我使用预定义的所有键的副本制作了一个 ConruentHashMap。 然后我循环遍历我的任务的所有结果,将它们也放入 ConcurrentHashMap 中,并使用预定义的键将所有结果添加到原始复制的 ConcurrentHashMap 中。 这是发生错误的时间(尽管只是有时,这表明它与线程有关,但我认为此时所有线程处理工作都已完成?)。

ConcurrentHashMap<String, HashSet<String>> headersHashsetConcurrent = new ConcurrentHashMap<>(headersHashset);

try {
    for (Future<HashMap<String, HashSet<String>>> f : futures) {
        ConcurrentHashMap<String, HashSet<String>> threadSafeItems = new ConcurrentHashMap<>(f.get());
        for (Map.Entry<String, HashSet<String>> items : threadSafeItems.entrySet()) {
            headersHashsetConcurrent.get(items.getKey()).addAll(items.getValue());  // ERROR SHOWS HERE 
        }
    }
} catch (Exception e) {
    e.printStackTrace();
}

这是完整的错误:

java.util.ConcurrentModificationException
    at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1584)
    at java.base/java.util.HashMap$KeyIterator.next(HashMap.java:1607)
    at java.base/java.util.AbstractCollection.addAll(AbstractCollection.java:335)
    at project/project.parseCSV.processFile(parseCSV.java:101)
    at project/project.parseCSV.call(parseCSV.java:126)
    at project/project.parseCSV.call(parseCSV.java:11)
    at javafx.graphics/javafx.concurrent.Task$TaskCallable.call(Task.java:1425)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base/java.lang.Thread.run(Thread.java:832)

我真的不知道为什么这会导致错误,因为 addAll 只在主线程上运行,而且它也插入到 ConCurrent HashMap 中 - 关于为什么会发生这种情况的任何想法将不胜感激!

问题是headersHashset.clone()不会在值中克隆HashSet

文档

返回此 HashMap 实例的浅表副本:不会克隆键和值本身。

这意味着localHeadersHashset在你的任务,你的headersHashsetConcurrent ,并threadSafeItems由归国futures -所有这些使用相同HashSet为同一键的对象。

由于任务是在并行线程中执行的,完全有可能在主线程对内部同一个HashSet的元素进行迭代时,某个任务同时执行了HashSet.add()

            headersHashsetConcurrent.get(items.getKey()).addAll(items.getValue());

这就是导致您的ConcurrentModificationException原因。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM