【发布时间】:2018-01-18 14:15:39
【问题描述】:
我正在尝试理解 SynchronizedMap 并运行了以下代码。我得到以下输出,但有一个例外。根据我的理解,当线程仍在对映射执行写入或线程处于睡眠状态时,get() 方法尝试访问同步映射时会导致异常。我的理解是正确的还是我遗漏了什么?
class MapHelper1 implements Runnable {
Map<String, Integer> map;
public MapHelper1(Map<String, Integer> map) {
this.map = map;
new Thread(this, "MapHelper1").start();
}
public void run() {
map.put("One", 1);
try {
System.out.println("MapHelper1 sleeping");
Thread.sleep(100);
} catch (Exception e) {
System.out.println(e);
}
}
}
class MapHelper2 implements Runnable {
Map<String, Integer> map;
public MapHelper2(Map<String, Integer> map) {
this.map = map;
new Thread(this, "MapHelper3").start();
}
public void run() {
map.put("two", 1);
try {
System.out.println("MapHelper2 sleeping");
Thread.sleep(100);
} catch (Exception e) {
System.out.println(e);
}
}
}
class MapHelper3 implements Runnable {
Map<String, Integer> map;
public MapHelper3(Map<String, Integer> map) {
this.map = map;
new Thread(this, "MapHelper3").start();
}
public void run() {
map.put("three", 1);
try {
System.out.println("MapHelper3 sleeping");
Thread.sleep(100);
} catch (Exception e) {
System.out.println(e);
}
}
}
public class MainClass {
public static void main(String[] args) {
Map<String, Integer> hashMap = new HashMap<>();
Map<String, Integer> syncMap = Collections.synchronizedMap(hashMap);
MapHelper1 mapHelper1 = new MapHelper1(syncMap);
MapHelper2 mapHelper2 = new MapHelper2(syncMap);
MapHelper3 mapHelper3 = new MapHelper3(syncMap);
for (Map.Entry<String, Integer> e : syncMap.entrySet()) {
System.out.println(e.getKey() + "=" + e.getValue());
}
}
}
输出:
MapHelper1 sleeping
MapHelper2 sleeping
MapHelper3 sleeping
Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1494)
at java.base/java.util.HashMap$EntryIterator.next(HashMap.java:1527)
at java.base/java.util.HashMap$EntryIterator.next(HashMap.java:1525)
at MainClass.main(MainClass.java:137)
Command exited with non-zero status 1
编辑:当输出无异常生成时,我再次运行代码几次。为什么会出现这种行为?
【问题讨论】:
-
这不是一个通用的地图迭代问题,而是一个
Collections.synchronizedMap问题。这是有效的。并回答:您必须在syncMap外部迭代上手动同步。查看synchronizedMap方法javadoc -
@Kayaman 我知道您链接到的问题是相关的,但是在这个问题中删除了哪些条目?请重新考虑打开这个问题,或者找到另一个真正的重复项
-
好的,我会重新打开这个。那你最好把一些好的答案放在那里。
-
@ghostrider 不,因为在 3 个 put 之间存在竞争条件,使迭代器超过了条目集。如果所有 3 个 put 在获取迭代器之前发生,那么它会运行正常,但你不能相信这总是会以这种方式发生
-
@ghostrider 是的,尽管读取线程可能会在一个写入操作和另一个写入操作之间启动,即没有保证顺序。同步映射对于读/写“原子”操作很有用。遍历整个地图不是“原子”读取操作,而是“批量”读取操作(以某种方式命名)。底层迭代器一次“原子地”获取一个条目。从这个意义上说,地图是同步的,但不是整个遍历。
标签: java multithreading concurrency synchronization