并发Hashmap - 失败安全问题

时间:2014-09-05 15:10:21

标签: java hashmap concurrenthashmap concurrentmodification

我正在尝试使用ConcurrentHashMap进行故障保护的示例。

以下是我试过的示例代码段。

ConcurrentHashMap<String, String> cMap = new ConcurrentHashMap<String, String>();
cMap.put("1", "Windows Phone");
cMap.put("2", "iPhone");
cMap.put("3", "HTC");

Iterator iterator=cMap.keySet().iterator();

while (iterator.hasNext()) {
    System.out.println(cMap.get(iterator.next()));
    cMap.put("Samsung", "S5");
}

输出结果为:

Windows Phone
HTC
iPhone

这是我理解的一个无故障安全的例子。

但是当我尝试下面的例子时,我得到了不同的输出。

ConcurrentHashMap<String, String> cMap = new ConcurrentHashMap<String, String>();
cMap.put("1", "Windows Phone");
cMap.put("2", "iPhone");
cMap.put("3", "HTC");

Iterator iterator=cMap.keySet().iterator();

while (iterator.hasNext()) {
    System.out.println(cMap.get(iterator.next()));
    cMap.put("4", "S5");
}

输出

Windows Phone
HTC
S5
iPhone

上述两个代码段之间有什么区别。在第二个代码段中,我添加 cMap.put(“4”,“S5”); ,这是添加的。但是在fisrt片段中,我添加了 cMap.put(“Samsung”,“S5”); ,它没有被添加到ConcurrentHashmap中。我是否犯了任何错误或者其他原因可能是这种不同的输出。

提前致谢。

3 个答案:

答案 0 :(得分:12)

与非并发hashmap相反,并发映射如果在迭代时添加内容,则不会快速失败。

但是,无法保证迭代器(或get()方法或任何其他读取操作)何时以及是否会看到新添加/删除的元素。

来自文档:

  

Iterators和Enumerations在迭代器/枚举的创建时或之后的某个时刻返回反映哈希表状态的元素

编辑:

连贯的不同结果背后的原因在于ConcurrentHashMap如何组织段并迭代它们。

根据密钥的哈希值将密钥映射到不同的段:

"1" -> 15
"2" -> 0
"3" -> 6

"4" -> 5
"Samsung" -> 7

当调用迭代器时,它会迭代从最后到第一个的段。

因此,在0时刻,迭代器从第15段开始,在第15段找到键&#34; 1&#34; =&#34; Windows phone&#34;,它首先输出。

然后,由于迭代器内部实现,它到达下一个元素,即&#34; 3&#34; =&#34; HTC&#34;在第6段。

此时插入&#34; S5&#34;到位。如果密钥是&#34; 4&#34;它将进入第5段,如果密钥是&#34;三星&#34;它会转到第7段。

发送&#34; HTC&#34;输出它搜索下一个元素。

当密钥为&#34; 4&#34;时,它进入第5段,找到&#34; 4&#34; =&#34; S5&#34;,并将其发送到输出。

如果键是&#34;三星&#34;,则该条目进入第7段,该段已被扫描并被迭代器发现为空,因此它找不到它并直接进入0段以检索&# 34; 2&#34; =&#34; iPhone&#34;

这解释了这种行为。

答案 1 :(得分:1)

  

但是在fisrt片段中,我添加了cMap.put(“三星”,“S5”);它没有被添加到ConcurrentHashmap。

这是不正确的。您 将其添加到哈希映射中。之后将其打印出来,您会看到该条目在那里。

但是,在迭代它时添加到哈希映射的项目可能会也可能不会被当前迭代器迭代。你的两个样本都展示了这两种行为。

java.util.concurrent包中的结构在迭代时没有检查修改,(你称之为“故障安全”),因为它们的整个点是同时使用的。普通集装箱甚至都没有保证。

答案 2 :(得分:0)

两个并行线程同时写入ConcurrentHashMap。根据ConcurrentHashMap的默认实现,最多16个线程可以写入&amp;并行阅读。但最糟糕的情况是,如果两个对象位于ConcurrentHashMap的同一段或分区中,则无法进行并行写入。