声明:迁移自本人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. 数据库连接需要dll

    连接oracle引用: Oracle.ManagedDataAccess.dll和Oracle.ManagedDataAccess.EntityFramework.dll, 连接sqlserver 连 ...

  2. Window Server 2019 配置篇(5)- 在域中建立WSUS以实现自动更新

    上次讲到我们的服务器群中增加了一台用于自动部署的服务器,这次我们要添加一台搭载WSUS服务的服务器,以实现对window更新的管理 那么WSUS是什么服务呢? WSUS是window server u ...

  3. Kafka--生产者

    一个应用程序在很多情况下需要往Kafka写入消息:记录用户的活动(用于审计和分析),记录度量指标,保存日志消息,记录智能家电的信息,与其他应用程序进行异步通信,缓冲即将写入到数据库的数据,等等. 多样 ...

  4. matlab和fortran混合编程

    matlab2016b+vs2010+ivf2013+f90 其实默认是f77语法,但通过配置可以改变为自由格式. 默认只能f77代码,怎样修改: https://ww2.mathworks.cn/m ...

  5. 052-PHP输出多个参数

    <?php $x=5; //初始化两个变量 $y=10; echo $x,$y,"<br />$x+$y=",$x+$y; //输出多个参数 ?>

  6. [APIO2012]派遣 可并堆

    Background 在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿. Description 在这个帮派里,有一名忍者被称之为Master.除了Master以外,每名忍者 ...

  7. Assignment写作谨慎学术抄袭是关键

    学术写作(Academic Writing)作为留学生涯的“必修课”,总是让闻者叹气,抓耳挠腮.初入课堂的留学生,更是缺乏写作经验不知从何下笔,只想仰天长啸“Essay真的好难啊!!”面对一个Essa ...

  8. Spark 内存管理

    Spark 内存管理 Spark 执行应用程序时, 会启动 Driver 和 Executor 两种 JVM 进程 Driver 负责创建 SparkContext 上下文, 提交任务, task的分 ...

  9. MongoDB Limit

    版权所有,未经许可,禁止转载 章节 MongoDB 入门 MongoDB 优势 MongoDB 安装 MongoDB 数据建模 MongoDB 创建数据库 MongoDB 删除数据库 MongoDB ...

  10. inkscape批量将svg转为pdf

    for i in *.svg;do inkscape --export-pdf=${i%.*}.pdf $i;done