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

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

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

  1. package com;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4. public class TestHashMapFastFail
  5. {
  6. public static void main(String[] args)
  7. {
  8. System.out.println("test HashMap fast-fail");
  9. Map<Integer, String> testHashMap = new HashMap<Integer, String>();
  10. testHashMap.put(1000, "1000");
  11. testHashMap.put(2000, "2000");
  12. testHashMap.put(3000, "3000");
  13. testHashMap.put(4000, "4000");
  14. testHashMap.put(5000, "5000");
  15. System.out.println(testHashMap.size());
  16. for (Map.Entry<Integer, String> entry : testHashMap.entrySet())
  17. {
  18. int key = entry.getKey();
  19. System.out.println("key=" + key);
  20. if (key == 3000)
  21. {
  22. testHashMap.remove(key);
  23. }
  24. }
  25. System.out.println(testHashMap.size());
  26. for (Map.Entry<Integer, String> entry : testHashMap.entrySet())
  27. {
  28. System.out.println(entry.getKey() + "-->" + entry.getValue());
  29. }
  30. }
  31. }

运行结果

  1. test HashMap
  2. 5
  3. key=2000
  4. key=4000
  5. key=1000
  6. key=3000
  7. Exception in thread "main" java.util.ConcurrentModificationException
  8. at java.util.HashMap$HashIterator.nextNode(HashMap.java:1437)
  9. at java.util.HashMap$EntryIterator.next(HashMap.java:1471)
  10. at java.util.HashMap$EntryIterator.next(HashMap.java:1469)
  11. at com.TestHashMapFastFail.main(TestHashMapFastFail.java:18)

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

再看ConcurrentHashMap的fail-safe

  1. package com;
  2. import java.util.Map;
  3. import java.util.concurrent.ConcurrentHashMap;
  4. public class TestConcurrentHashMapFailSafe
  5. {
  6. public static void main(String[] args)
  7. {
  8. System.out.println("test ConcurrentHashMap fast-fail");
  9. Map<Integer, String> testConcurrentHashMap = new ConcurrentHashMap<Integer, String>();
  10. testConcurrentHashMap.put(100, "100");
  11. testConcurrentHashMap.put(200, "200");
  12. testConcurrentHashMap.put(300, "300");
  13. testConcurrentHashMap.put(400, "400");
  14. testConcurrentHashMap.put(500, "500");
  15. System.out.println(testConcurrentHashMap.size());
  16. for (Map.Entry<Integer, String> entry : testConcurrentHashMap.entrySet())
  17. {
  18. int key = entry.getKey();
  19. System.out.println("key=" + key);
  20. if (key == 300)
  21. {
  22. testConcurrentHashMap.remove(key);
  23. }
  24. }
  25. System.out.println(testConcurrentHashMap.size());
  26. for (Map.Entry<Integer, String> entry : testConcurrentHashMap.entrySet())
  27. {
  28. System.out.println(entry.getKey() + "-->" + entry.getValue());
  29. }
  30. }
  31. }

运行结果

  1. test ConcurrentHashMap fast-fail
  2. 5
  3. key=400
  4. key=100
  5. key=500
  6. key=200
  7. key=300
  8. 3
  9. 100-->100
  10. 500-->500
  11. 300-->300

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

  1. package com;
  2. import java.util.HashMap;
  3. import java.util.Iterator;
  4. import java.util.Map;
  5. public class TestHashMapFastFail2
  6. {
  7. public static void main(String[] args)
  8. {
  9. System.out.println("test solve HashMap fast-fail");
  10. Map<Integer, String> testHashMap = new HashMap<Integer, String>();
  11. testHashMap.put(1000, "1000");
  12. testHashMap.put(2000, "2000");
  13. testHashMap.put(3000, "3000");
  14. testHashMap.put(4000, "4000");
  15. testHashMap.put(5000, "5000");
  16. System.out.println(testHashMap.size());
  17. Iterator iterator = testHashMap.entrySet().iterator();
  18. while (iterator.hasNext())
  19. {
  20. int key = (int)((Map.Entry)iterator.next()).getKey();
  21. System.out.println("key=" + key);
  22. if (key == 2000 || key == 4000)
  23. {
  24. iterator.remove();
  25. }
  26. }
  27. System.out.println(testHashMap.size());
  28. for (Map.Entry<Integer, String> entry : testHashMap.entrySet())
  29. {
  30. System.out.println(entry.getKey() + "-->" + entry.getValue());
  31. }
  32. }
  33. }

运行结果

  1. test solve HashMap fast-fail
  2. 5
  3. key=2000
  4. key=4000
  5. key=1000
  6. key=3000
  7. key=5000
  8. 3
  9. 1000-->1000
  10. 3000-->3000
  11. 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. SwiftUI中多设备运行方法

    https://blog.csdn.net/weixin_42679753/article/details/94465674 https://www.jianshu.com/p/17fc7929fcb ...

  2. CAN网络上新增加的设备与网络上已有设备MAC地址冲突的软件解决方案

    已知 1号的CAN节点的地址是0x1f 2号的CAN 节点的地址是0x1f 要达到的要求是 假设 网络上 CAN1 节点已经工作了,我现在需要在网络上接入CAN2节点. 那么CAN2节点首次上电的时候 ...

  3. python学习笔记2018-9-18

    1.可选参数传递 此处m=1并不是写定m必为1,而是m为可选参数,当不对其进行赋值时,其默认值为1. 2.函数的返回值 return可以传递0个返回值,也可以传递任意多个返回值 3.局部变量与全局变量 ...

  4. scala文件通过本地命令运行

    1.准备(检查) a.本地环境安装jdk b.安装scala 2.sublime编辑scala文件,并存放到F:\plan_next\scala_compile下 3.文件目录中切换到cmd中(文件目 ...

  5. CentOS7基于http方式搭建本地yum源

    1.创建yum软件保存目录[root@localhost ~]# mkdir -p /www/share/yum 2. 修改yum配置文件先备份yum配置文件,修改yum配置文件中yum软件包保存目录 ...

  6. 19 03 13 关于 scrapy 框架的 对环球网的整体爬取(存储于 mongodb 数据库里)

    关于  spinder  在这个框架里面   和不用数据库  相同 # -*- coding: utf-8 -*- import scrapy from yang_guan.items import ...

  7. swoole在线聊天学习笔记

    <?php $http=); $http->on('request',function(swoole_http_request $request,swoole_http_response ...

  8. 七、JavaScript之console.log输出和document.write输出

    一.代码如下 二.运行效果如下 三.点击之后,效果如下 四.按一下F12,在控制台中可以看到

  9. Swift Json解析基础

    func JSONToData(obj:Any) -> Data { //先判断是否可以转换 if !JSONSerialization.isValidJSONObject(obj) { ret ...

  10. Essay写作的五大陷阱如何避免?

    相信ESSAY写作对留学生来说印象非常深刻,由于国外不同的教育模式,老师动不动就是一篇essay.可是在大家都拥有相同的GMAT或者GPA以及雅思分数的情况下.大家如何才能够脱颖而出呢?下面BayDu ...