ConcurrentHashMap returns null 如果我删除下一个对象
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
。
此映射不会抛出此异常,而是 return keys/values 反映自创建迭代器以来某个时间跨度内存在的异常,以及当前时间。 javadoc:
中也说明了这一点
Retrieval operations (including get) generally do not block, so may overlap with update operations (including put and remove). Retrievals reflect the results of the most recently completed update operations holding upon their onset. For aggregate operations such as putAll and clear, concurrent retrievals may reflect insertion or removal of only some entries. Similarly, Iterators and Enumerations return elements reflecting the state of the hash table at some point at or since the creation of the iterator/enumeration. They do not throw ConcurrentModificationException. However, iterators are designed to be used by only one thread at a time.
如果您需要同时使用值和键,而不会引起这样的错误,您应该遍历地图的入口集而不是键集,这可以通过以下方式完成:
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 的迭代器以线程安全的方式导航底层数据结构。它作为单次传递执行此操作,如果它具有 "passed" 的 key/entry 它不会看到该更改。如果更改发生在迭代器到达的点之外,如果它在到达那里之前没有再次更改,它可能会看到该更改。这是一个例子;
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]
注意如果不是在任何给定时刻设置的键,迭代器如何查看键。相反,之后发生的更改(在本例中为较大的数字)是可见的,但过去的键(在本例中为较小的数字)不可见。
我正在尝试了解故障安全迭代器的工作原理。我在并发哈希图中遇到了一些奇怪的事情。代码如下--
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
。
此映射不会抛出此异常,而是 return keys/values 反映自创建迭代器以来某个时间跨度内存在的异常,以及当前时间。 javadoc:
中也说明了这一点Retrieval operations (including get) generally do not block, so may overlap with update operations (including put and remove). Retrievals reflect the results of the most recently completed update operations holding upon their onset. For aggregate operations such as putAll and clear, concurrent retrievals may reflect insertion or removal of only some entries. Similarly, Iterators and Enumerations return elements reflecting the state of the hash table at some point at or since the creation of the iterator/enumeration. They do not throw ConcurrentModificationException. However, iterators are designed to be used by only one thread at a time.
如果您需要同时使用值和键,而不会引起这样的错误,您应该遍历地图的入口集而不是键集,这可以通过以下方式完成:
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 的迭代器以线程安全的方式导航底层数据结构。它作为单次传递执行此操作,如果它具有 "passed" 的 key/entry 它不会看到该更改。如果更改发生在迭代器到达的点之外,如果它在到达那里之前没有再次更改,它可能会看到该更改。这是一个例子;
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]
注意如果不是在任何给定时刻设置的键,迭代器如何查看键。相反,之后发生的更改(在本例中为较大的数字)是可见的,但过去的键(在本例中为较小的数字)不可见。