
[英]How Can I remove an item from ConcurrentHashMap when no have a thread holding certain object anymore?
[英]ConcurrentHashMap returns null if I remove the very next object
我试图了解故障安全迭代器的工作原理。 我在并发哈希图中遇到了一些奇怪的事情。 代码如下-
ConcurrentHashMap<String,String> hm = new ConcurrentHashMap<String,String>();
hm.put("name1", "value1");
hm.put("name2", "value2");
hm.put("name3", "value3");
Iterator<String> itr = hm.keySet().iterator();
int index = 0;
while(itr.hasNext()) {
System.out.println(hm.get(itr.next()));
hm.put(index+"1", index+"2");
hm.remove("name2");
index++;
}
System.out.println("--- Second iteration --- ");
Iterator<String> itr2 = hm.keySet().iterator();
while(itr2.hasNext()) {
System.out.println(hm.get(itr2.next()));
}
打印:
value3
null
value1
--- Second iteration ---
12
02
value3
value1
22
我很困惑为什么在第一种情况下删除元素时更新了,而没有添加! 我正在使用1.8运行时环境。
这是因为在编辑地图时, ConcurrentHashMap
的迭代器不会引发ConcurrentModificationException
。
该映射将返回反映自创建迭代器以来的某个时间段存在的键/值以及当前时间,而不是引发此异常。 这在javadoc中也有说明:
检索操作(包括get)通常不会阻塞,因此可能与更新操作(包括put和remove)重叠。 检索反映了自发生以来最新完成的更新操作的结果。 对于诸如putAll和clear的聚合操作,并发检索可能仅反映某些条目的插入或删除。 类似地,迭代器和枚举返回在创建迭代器/枚举时或此后某个时刻反映哈希表状态的元素。 他们不抛出ConcurrentModificationException。 但是,迭代器被设计为一次只能由一个线程使用。
如果您需要同时使用值和键,而又不会引起此类错误,则应遍历地图的条目集而不是键集,这可以通过以下操作完成:
Iterator<Map.Entry<String,String>> itr = hm.entrySet().iterator();
int index = 0;
while(itr.hasNext()) {
Map.Entry<String,String> next = itr.next();
System.out.println(next.getValue());
hm.put(index+"1", index+"2");
hm.remove("name2");
index++;
}
ConcurrentHashMap的Iterator以线程安全的方式导航基础数据结构。 它以单次通过的方式执行此操作,如果“通过”了键/条目,则不会看到该更改。 如果更改发生在Iterator所达到的范围之外,则它可能会看到该更改,如果在到达该位置之前没有再次更改。 这是一个例子;
Map<Integer, Boolean> map = new ConcurrentHashMap<>();
for (int i = 0; i < 10; i++)
map.put(i, true);
System.out.println(map.keySet());
List<Integer> ints = new ArrayList<>(map.keySet());
Map<Integer, Boolean> map2 = new ConcurrentHashMap<>();
for (int i = 0; i < 10; i += 2)
map2.put(ints.get(i), false);
System.out.println("All evens " + map2.keySet());
// all evens
Iterator<Integer> iter = map2.keySet().iterator();
for (int i = 8; i >= 0; i -= 2) {
// remove evens and add odds
map2.remove(ints.get(8 - i));
map2.remove(ints.get(i));
map2.put(ints.get(i + 1), false);
System.out.println(iter.next() +" - full set is: "+map2.keySet());
}
while (iter.hasNext())
System.out.println(iter.next());
System.out.println("All odds " + map2.keySet());
版画
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
All evens [0, 2, 4, 6, 8]
0 - full set is: [2, 4, 6, 9]
2 - full set is: [4, 7, 9]
4 - full set is: [5, 7, 9]
5 - full set is: [3, 5, 7, 9]
7 - full set is: [1, 3, 5, 7, 9]
9
All odds [1, 3, 5, 7, 9]
请注意,如果在任何给定时刻未设置键,则迭代器如何查看键。 而是可以看到在此之后发生的更改(在这种情况下为更高的数字),而在过去的键(在这种情况下为更低的数字)则不可见。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.