HashMap的fast-fail和ConcurrentHashMap的fail-safe实例
声明:迁移自本人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实例的更多相关文章
- HashMap底层原理以及与ConCurrentHashMap区别
HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象.当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bu ...
- HashMap不安全后果及ConcurrentHashMap线程安全原理
Java集合HashMap不安全后果及ConcurrentHashMap 原理 目录 HashMap JDK7 HashMap链表循环造成死循环 HashMap数据丢失 JDK7 Concurrent ...
- HashMap、Hash Table、ConcurrentHashMap
这个这个...本王最近由于开始找实习工作了,所以就在牛客网上刷一些公司的面试题,大多都是一些java,前端HTML,js,jquery,以及一些好久没有碰的算法题,说实话,有点难受,其实在我不知道的很 ...
- 非线程安全的HashMap 和 线程安全的ConcurrentHashMap
在平时开发中,我们经常采用HashMap来作为本地缓存的一种实现方式,将一些如系统变量等数据量比较少的参数保存在HashMap中,并将其作为单例类的一个属性.在系统运行中,使用到这些缓存数据,都可以直 ...
- 020-并发编程-java.util.concurrent之-jdk6/7/8中ConcurrentHashMap、HashMap分析
一.概述 哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表. 是根据关键码值(Key ...
- BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]
2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 2545 Solved: 1419[Submit][Sta ...
- BZOJ 3172: [Tjoi2013]单词 [AC自动机 Fail树]
3172: [Tjoi2013]单词 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 3198 Solved: 1532[Submit][Status ...
- fail树
前置技能:AC自动机 假设我们有了一个AC自动机,然后在上面进行字符串匹配. 上面是一个有四个字符串的AC自动机(abcde.aacdf.cdf.cde),虚线是fail指针,实线是转移. 这是上一次 ...
- 【BZOJ 2434】【NOI 2011】阿狸的打字机 fail树
完全不会啊,看题解还看了好久,我是蒟蒻$QAQ$ $zyf$的题解挺好的:http://blog.csdn.net/clove_unique/article/details/51059425 $fai ...
- BZOJ2434 [Noi2011]阿狸的打字机(AC自动机 + fail树 + DFS序 + 线段树)
题目这么说的: 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现,这个打字机是这样工作的: 输入小 ...
随机推荐
- ②java基础——标识符、关键字和基础数据类型
Java标识符: 1.由英文字母.数字._(下划线)和$组成,长度不限.其中英文字母包含大写字母(A~Z)和小写字母(a~z),数字包含0到9 2.标识符的第一个字符不能是数字(即标识符不能以数字开头 ...
- BBS那些事儿
目录 1 注册 2 登陆 3 图片验证码相关 4 首页相关,Django Admin后台录入数据 5 注销功能 6 修改密码 7 用户头像展示,media配置 8 个人站点,个人侧边栏 9 侧边栏筛选 ...
- HDU - 6205 card card card (尺取法)
题意:有n堆牌,ai表示每堆牌的牌数,bi表示每堆牌的penaltyvalue,操作开始前,可以重复进行将第一堆牌挪到最后一堆这一操作.然后,对于挪完后的牌,从第一堆开始,依次取.对于每一堆牌,首先将 ...
- Ubuntu18.04可执行文件运行提示No such file or directory
参考原文见 https://blog.csdn.net/sun927/article/details/46593129? 最近我也遇到一个类似问题,同一个工具在Ubuntu16.04里面运行是好的,但 ...
- 十二、Sap的压缩类型p的使用方法
一.代码如下 二.我们查看输出结果 三.如果位数超出了会怎样呢?我们试试 四.提示如下
- Swift 元组 Tuple
let infoArray:[Any] = ["jack",18,1.88] let infoName=infoArray[0] as!String //此处为Any类型强转为St ...
- Mac电脑上怎么设置环境变量
https://jingyan.baidu.com/article/8065f87f47b29523312498e4.html 环境变量是电脑操作系统中常用的一些变量,作用类似于将一些常用命令所在的文 ...
- 设置虚拟机静态ip
1.查看本机ip.网关.dns服务器 IPv4地址:本机局域网ip 路由器:网关地址 nameserver:局域网内部dns服务器 其他dns服务器 移动.电信和联通:114.114.114.114 ...
- C# NPOI的数据批量导入数据库
public ActionResult Upload(HttpPostedFileBase Namefile) { //判断文件是否存在 if ...
- 【LeetCode】509. 斐波那契数
题目 斐波那契数,通常用 F(n) 表示,形成的序列称为斐波那契数列.该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和.也就是: F(0) = 0, F(1) = 1 F(N) = ...