声明:迁移自本人CSDN博客https://blog.csdn.net/u013365635

Java HashMap遍历过程中如果元素被修改会导致遍历失败,ConcurrentHashMap则不会有这个问题。由此引出HashMap的fast-fail机制和ConcurrentHashMap的的fail-safe机制。

看如下实例。
首先看HashMap的fast-fail

package com;

import java.util.HashMap;
import java.util.Map; public class TestHashMapFastFail
{
public static void main(String[] args)
{
System.out.println("test HashMap fast-fail");
Map<Integer, String> testHashMap = new HashMap<Integer, String>();
testHashMap.put(1000, "1000");
testHashMap.put(2000, "2000");
testHashMap.put(3000, "3000");
testHashMap.put(4000, "4000");
testHashMap.put(5000, "5000");
System.out.println(testHashMap.size());
for (Map.Entry<Integer, String> entry : testHashMap.entrySet())
{
int key = entry.getKey();
System.out.println("key=" + key);
if (key == 3000)
{
testHashMap.remove(key);
}
}
System.out.println(testHashMap.size());
for (Map.Entry<Integer, String> entry : testHashMap.entrySet())
{
System.out.println(entry.getKey() + "-->" + entry.getValue());
}
}
}

运行结果

test HashMap
5
key=2000
key=4000
key=1000
key=3000
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextNode(HashMap.java:1437)
at java.util.HashMap$EntryIterator.next(HashMap.java:1471)
at java.util.HashMap$EntryIterator.next(HashMap.java:1469)
at com.TestHashMapFastFail.main(TestHashMapFastFail.java:18)

可以看到执行remove操作后,下一轮迭代立刻失效,并抛出异常,这就是所谓的fast-fail。

再看ConcurrentHashMap的fail-safe

package com;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; public class TestConcurrentHashMapFailSafe
{
public static void main(String[] args)
{
System.out.println("test ConcurrentHashMap fast-fail");
Map<Integer, String> testConcurrentHashMap = new ConcurrentHashMap<Integer, String>();
testConcurrentHashMap.put(100, "100");
testConcurrentHashMap.put(200, "200");
testConcurrentHashMap.put(300, "300");
testConcurrentHashMap.put(400, "400");
testConcurrentHashMap.put(500, "500");
System.out.println(testConcurrentHashMap.size());
for (Map.Entry<Integer, String> entry : testConcurrentHashMap.entrySet())
{
int key = entry.getKey();
System.out.println("key=" + key);
if (key == 300)
{
testConcurrentHashMap.remove(key);
}
}
System.out.println(testConcurrentHashMap.size());
for (Map.Entry<Integer, String> entry : testConcurrentHashMap.entrySet())
{
System.out.println(entry.getKey() + "-->" + entry.getValue());
}
}
}

运行结果

 test ConcurrentHashMap fast-fail
5
key=400
key=100
key=500
key=200
key=300
3
100-->100
500-->500
300-->300

可以看出,尽管在迭代过程中执行了remove操作,但是ConcurrentHashMap对外的表现仍然正常,这就是所谓的fail-safe。原因在于ConcurrentHashMap返回的迭代器是弱一致性,ConcurrentHashMap底层数据结构改变时并且不会抛出ConcurrentModificationException异常。
所以,这也是选择ConcurrentHashMap可以获得的一个额外好处,或者说是解决HashMap fast-fail的一种方法,还有一个方法就是使用迭代器的remove方法而不是使用集合的remove方法,示例如下。

package com;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map; public class TestHashMapFastFail2
{
public static void main(String[] args)
{
System.out.println("test solve HashMap fast-fail");
Map<Integer, String> testHashMap = new HashMap<Integer, String>();
testHashMap.put(1000, "1000");
testHashMap.put(2000, "2000");
testHashMap.put(3000, "3000");
testHashMap.put(4000, "4000");
testHashMap.put(5000, "5000");
System.out.println(testHashMap.size());
Iterator iterator = testHashMap.entrySet().iterator();
while (iterator.hasNext())
{
int key = (int)((Map.Entry)iterator.next()).getKey();
System.out.println("key=" + key);
if (key == 2000 || key == 4000)
{
iterator.remove();
}
}
System.out.println(testHashMap.size());
for (Map.Entry<Integer, String> entry : testHashMap.entrySet())
{
System.out.println(entry.getKey() + "-->" + entry.getValue());
}
}
}

运行结果

test solve HashMap fast-fail
5
key=2000
key=4000
key=1000
key=3000
key=5000
3
1000-->1000
3000-->3000
5000-->5000

集合的 fast-fail 问题是初学者很容易犯的错误。
说说fast-fail机制和fail-safe机制设计的原因。有人可能会问,既然fast-fail有这么多弊端,为什么还要设计呢,以HashMap为例,因为HashMap本身就是设计成线程不安全的,不支持多个线程同时安全修改,但这也意味着HashMap有较快的速度。fail-safe机制设计的初衷就是保证多线程并发安全地修改集合或Map类。当然,本文的用例都是单线程中的修改操作,主要是为了引出这2个概念。至于内部实现机制,看源码吧。

HashMap的fast-fail和ConcurrentHashMap的fail-safe实例的更多相关文章

  1. HashMap底层原理以及与ConCurrentHashMap区别

    HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象.当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bu ...

  2. HashMap不安全后果及ConcurrentHashMap线程安全原理

    Java集合HashMap不安全后果及ConcurrentHashMap 原理 目录 HashMap JDK7 HashMap链表循环造成死循环 HashMap数据丢失 JDK7 Concurrent ...

  3. HashMap、Hash Table、ConcurrentHashMap

    这个这个...本王最近由于开始找实习工作了,所以就在牛客网上刷一些公司的面试题,大多都是一些java,前端HTML,js,jquery,以及一些好久没有碰的算法题,说实话,有点难受,其实在我不知道的很 ...

  4. 非线程安全的HashMap 和 线程安全的ConcurrentHashMap

    在平时开发中,我们经常采用HashMap来作为本地缓存的一种实现方式,将一些如系统变量等数据量比较少的参数保存在HashMap中,并将其作为单例类的一个属性.在系统运行中,使用到这些缓存数据,都可以直 ...

  5. 020-并发编程-java.util.concurrent之-jdk6/7/8中ConcurrentHashMap、HashMap分析

    一.概述 哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表. 是根据关键码值(Key ...

  6. BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]

    2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2545  Solved: 1419[Submit][Sta ...

  7. BZOJ 3172: [Tjoi2013]单词 [AC自动机 Fail树]

    3172: [Tjoi2013]单词 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 3198  Solved: 1532[Submit][Status ...

  8. fail树

    前置技能:AC自动机 假设我们有了一个AC自动机,然后在上面进行字符串匹配. 上面是一个有四个字符串的AC自动机(abcde.aacdf.cdf.cde),虚线是fail指针,实线是转移. 这是上一次 ...

  9. 【BZOJ 2434】【NOI 2011】阿狸的打字机 fail树

    完全不会啊,看题解还看了好久,我是蒟蒻$QAQ$ $zyf$的题解挺好的:http://blog.csdn.net/clove_unique/article/details/51059425 $fai ...

  10. BZOJ2434 [Noi2011]阿狸的打字机(AC自动机 + fail树 + DFS序 + 线段树)

    题目这么说的: 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现,这个打字机是这样工作的: 输入小 ...

随机推荐

  1. gem5-gpu 全系统FS模式 系统调用SE模式

    SE模式中无线程调度器,只能运行单线程程序,如SPEC CPU 2006,仅模拟片上CPU.GPU.Network和DRAM等. FS模式需加载虚拟Linux和磁盘,Linux负责线程调度.实现了Pt ...

  2. JuJu alpha

    JuJu alpha阶段总结博客 JuJu   设想与目标   我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 在cao ying researcher给的资料中定 ...

  3. (八)微信小程序---获取定位信息chooseLocation

    微信小程序---获取定位信息  chooseLocation wxml <view bindtap="getlocalPath">{{localPath}}</v ...

  4. linux桌面系统 镜像下载

    1.Ubuntu 官方下载地址(不推荐,网速较慢):https://www.ubuntu.com/download 阿里云:http://mirrors.aliyun.com/ubuntu-relea ...

  5. 剑指offer自学系列(四)

    题目描述: 输入一个正整数数组,把数组里面所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个,例如输入数组{3,32,321},输出的最小数字为321323 题目分析: 如果采用穷举法,把 ...

  6. P1037 在霍格沃茨找零钱

    转跳点:

  7. 七十二、SAP中内表的修改,添加条件语句,多条目修改

    一.代码如下 二.MODIFY执行前断点数据 三.查看到数据如下 四.往下单步走一步,发现有3条数据被修改 五.执行后修改如下

  8. 吴裕雄--天生自然C++语言学习笔记:C++ 预处理器

    预处理器是一些指令,指示编译器在实际编译之前所需完成的预处理. 所有的预处理器指令都是以井号(#)开头,只有空格字符可以出现在预处理指令之前.预处理指令不是 C++ 语句,所以它们不会以分号(;)结尾 ...

  9. jQuery中:first,:first-child,first()的使用区别

    ul li:first  先获取页面中所有li节点对象数组,然后返回数组中的第一个li节点对象 . :first-child  选择器选取属于其父元素的第一个子元素的所有元素. first() 返回被 ...

  10. UVA - 12627 Erratic Expansion(奇怪的气球膨胀)(递归)

    题意:问k小时后,第A~B行一共有多少个红气球. 分析:观察图可发现,k小时后,图中最下面cur行的红气球个数满足下式: (1)当cur <= POW[k - 1]时, dfs(k, cur) ...