当多个ConcurrentModificationException
读写HashMap
的实例时,我一直试图重现(和解决)Thread
。
免责声明:我知道HashMap
不是线程安全的。
在以下代码中:
import java.util.*;
public class MyClass {
public static void main(String args[]) throws Exception {
java.util.Map<String, Integer> oops = new java.util.HashMap<>();
oops.put("1", 1);
oops.put("2", 2);
oops.put("3", 3);
Runnable read = () -> {
System.out.println("Entered read thread");
/*
* ConcurrentModificationException possibly occurs
*
for (int i = 0; i < 100; i++) {
List<Integer> numbers = new ArrayList<>();
numbers.addAll(oops.values());
System.out.println("Size " + numbers.size());
}
*/
for (int i = 0; i < 100; i++) {
List<Integer> numbers = new ArrayList<>();
numbers.addAll(oops.values()
.stream()
.collect(java.util.stream.Collectors.toList()));
System.out.println("Size " + numbers.size());
}
};
Runnable write = () -> {
System.out.println("Entered write thread");
for (int i = 0; i < 100; i++) {
System.out.println("Put " + i);
oops.put(Integer.toString(i), i);
}
};
Thread writeThread = new Thread(write, "write-thread");
Thread readThread = new Thread(read, "read-thread");
readThread.start();
writeThread.start();
readThread.join();
writeThread.join();
}
}
基本上,我有两个线程:一个不断将元素放入HashMap
,另一个正在HashMap.values()
上迭代。
在read
线程中,如果我使用的是numbers.addAll(oops.values())
,则ConcurrentModificationException
会随机出现。尽管这些行是按预期随机打印的。
但是,如果我切换到numbers.addAll(oops.values().stream()..
,则不会出现任何错误。但是,我发现了一个奇怪的现象。由read
线程打印的所有行在之后被打印。
我的问题是write
是否具有内部同步功能?
更新:
使用JDoodle https://www.jdoodle.com/a/IYy,看来在JDK9和JDK10上,我会按预期得到Collection.stream()
。
谢谢!
答案 0 :(得分:1)
我能够在Java 8上使用流获得ConcurrentModificationException
,但代码有所变化:迭代次数增加,要映射到单独线程中的元素数量也从100增加到10000。并且还添加了{{ 1}},以使读写器线程中的循环或多或少地同时开始。我还检查了CyclicBarrier
分离器的源代码,如果对地图做了一些修改,它将抛出Hashmap.values()
。
ConcurrentModificationException
答案 1 :(得分:1)
您所看到的完全是偶然;请记住,内部System.out.println
会执行synchronzied
;因此可能是 使结果看起来像按顺序显示。
我还没有深入研究您的代码-因为分析为什么HashMap
(不是线程安全的)为什么会错过行为,这很可能是徒劳的;如您所知,它被证明是非线程安全的。
关于该ConcurrentModificationException
的文档特别指出,它将尝试尽最大可能抛出该错误;所以在这一点上可能是Java-8较弱,或者又是偶然。
答案 2 :(得分:0)
我快速查看了Java 8的源代码,它确实抛出了--user
。
id
的{{1}}方法返回ConcurrentModificationException
的子类,该子类的HashMap
方法返回一个values()
并抛出AbstractCollection
。
有关信息,spliterator()
使用分隔符来遍历或划分源元素。