繁体   English   中英

如果我删除下一个对象,则ConcurrentHashMap返回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

该映射将返回反映自创建迭代器以来的某个时间段存在的键/值以及当前时间,而不是引发此异常。 这在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.

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