JAVA学习:HashMap 和 ConcurrentHashMap
一、最基本的HashMap 和 ConcurrentHashMap
1、HashMap的结构和底层原理:由数组和链表组成,数组里面每个地方都存了Key-Value这样的实例,在Java7叫Entry在Java8中叫Node
在JDK1.6,JDK1.7中,HashMap采用位桶+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,HashMap采用位桶+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。
2、简单说下HashMap的实现原理:
首先有一个每个元素都是链表(可能表述不准确)的数组,当添加一个元素(key-value)时,就首先计算元素key的hash值,以此确定插入数组中的位置,但是可能存在同一hash值的元素已经被放在数组同一位置了,这时就添加到同一hash值的元素的后面,他们在数组的同一位置,但是形成了链表,同一各链表上的Hash值是相同的,所以说数组存放的是链表。而当链表长度太长时,链表就转换为红黑树,这样大大提高了查找的效率。
当链表数组的容量超过初始容量的0.75时,再散列将链表数组扩大2倍,把原链表数组的搬移到新的数组中
3、Hashmap在多线程环境下存在线程安全问题,那一般怎么处理这种情况:
(1)使用Collections.synchronizedMap(Map)创建线程安全的map集合;
(2)Hashtable
(3)ConcurrentHashMap(性能和效率都高于前者)
4、Collections.synchronizedMap(Map)是怎么实现线程安全的
synchronizedMap内部维护了一个普通对象map,还有排斥锁mutex,如图:
调用这个方法时需要传入一个Map,可以看到有两个构造器,如果你传入了mutex参数,则将对象排斥锁赋值为传入的对象。如果没有,则将对象排斥锁赋值给this,即调用synchronizedMap的对象,就是上面的Map。创建出synchronizedMap之后,再操作map的时候,就会对方法上锁,如图:
5、Hashtable与HashMap的区别
Hashtable对数据操作的时候都会上锁,因此效率比较低。
(1)实现方式不同:Hashtable继承了Dictionary类,而HashMap继承的是AbstractMap类。
Dictionary 是 JDK 1.0 添加的,貌似没人用过这个,我也没用过。
(2)初始化容量不同:HashMap 的初始容量为:16,Hashtable 初始容量为:11,两者的负载因子默认都是:0.75。
(3)扩容机制不同:当现有容量大于总容量 * 负载因子时,HashMap 扩容规则为当前容量翻倍,Hashtable 扩容规则为当前容量翻倍 + 1。
(4)迭代器不同:HashMap 中的 Iterator 迭代器是 fail-fast 的,而 Hashtable 的 Enumerator 不是 fail-fast 的。
6、fail-fast:
快速失败(fail—fast)是java集合中的一种机制, 在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出Concurrent Modification Exception。
原理:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变modCount的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。
注:这里异常的抛出条件是检测到 modCount!=expectedmodCount 这个条件。如果集合发生变化时修改modCount值刚好又设置为了expectedmodCount值,则异常不会抛出。因此,不能依赖于这个异常是否抛出而进行并发操作的编程,这个异常只建议用于检测并发修改的bug。
使用场景:java.util包下的集合类都是快速失败机制的, 不能在多线程下发生并发修改(迭代过程中被修改).
7、fail-safe
安全失败(fail—safe):采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先copy原有集合内容,在拷贝的集合上进行遍历.
原理:由于迭代时是对原集合的拷贝的值进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会出发ConcurrentModificationException
缺点:基于拷贝内容的优点是避免了ConcurrentModificationException
,但同样地, 迭代器并不能访问到修改后的内容 (简单来说就是, 迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的)
使用场景:java.util.concurrent包下的容器都是安全失败的,可以在多线程下并发使用,并发修改.
二、ConcurrentHashMap
1、ConcurrentHashMap使用分段锁技术,将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问能够实现真正的并发访问。如下图是ConcurrentHashMap的内部结构图:
从图中可以看到,ConcurrentHashMap内部分为很多个Segment,每一个Segment拥有一把锁,然后每个Segment(继承ReentrantLock)下面包含很多个HashEntry列表数组。对于一个key,需要经过三次(为什么要hash三次下文会详细讲解)hash操作,才能最终定位这个元素的位置,这三次hash分别为:
(1)对于一个key,先进行一次hash操作,得到hash值h1,也即h1 = hash1(key);
(2)将得到的h1的高几位进行第二次hash,得到hash值h2,也即h2 = hash2(h1高几位),通过h2能够确定该元素的放在哪个Segment;
(3)将得到的h1进行第三次hash,得到hash值h3,也即h3 = hash3(h1),通过h3能够确定该元素放置在哪个HashEntry。
每当一个线程占用锁访问一个 Segment 时,不会影响到其他的 Segment。就是说如果容量大小是16他的并发度就是16,可以同时允许16个线程操作16个Segment而且还是线程安全的。
2、注意事项
- ConcurrentHashMap中的key和value值都不能为null。
- ConcurrentHashMap的整个操作过程中大量使用了Unsafe类来获取Segment/HashEntry,这里Unsafe的主要作用是提供原子操作。Unsafe这个类比较恐怖,破坏力极强,一般场景不建议使用,如果有兴趣可以到这里做详细的了解Java中鲜为人知的特性
- ConcurrentHashMap是线程安全的类并不能保证使用了ConcurrentHashMap的操作都是线程安全的!
三、本文参考
链接:https://blog.csdn.net/qq_35190492/article/details/103589011
JAVA学习:HashMap 和 ConcurrentHashMap的更多相关文章
- 【转】Java学习---HashMap的工作原理
[原文]https://www.toutiao.com/i6592560649652404744/ HashMap的工作原理是近年来常见的Java面试题.几乎每个Java程序员都知道HashMap,都 ...
- Java学习——HashMap
遍历 Map map = new HashMap(); Iterator iter = map.entrySet().iterator(); while (iter.hasNext()) { Map. ...
- Java学习----HashMap原理
1.HashMap的数据结构 数组的特点是:寻址容易,插入和删除困难:而链表的特点是:寻址困难,插入和删除容易.那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?答案是肯定的, ...
- java基础知识再学习--HashMap与ConcurrentHashMap的区别
引用:http://blog.csdn.net/xuefeng0707/article/details/40834595 从JDK1.2起,就有了HashMap,正如前一篇文章所说,HashMap不是 ...
- java的HashMap与ConcurrentHashMap
好像今天没有什么源码读,那么就来看看java的这两种HashMap有啥不一样的地方吧,在这之前先普及一下HashMap的一些基本知识: (1)放入HashMap的元素是key-value对. (2)底 ...
- Java集合——HashMap,HashTable,ConcurrentHashMap区别
Map:“键值”对映射的抽象接口.该映射不包括重复的键,一个键对应一个值. SortedMap:有序的键值对接口,继承Map接口. NavigableMap:继承SortedMap,具有了针对给定搜索 ...
- Java中HashMap与ConcurrentHashMap的区别
从JDK1.2起,就有了HashMap,正如前一篇文章所说,HashMap不是线程安全的,因此多线程操作时需要格外小心. 在JDK1.5中,伟大的Doug Lea给我们带来了concurrent包,从 ...
- 【转】Java学习---HashMap和HashSet的内部工作机制
[原文]https://www.toutiao.com/i6593863882484220430/ HashMap和HashSet的内部工作机制 HashMap 和 HashSet 内部是如何工作的? ...
- [Java集合] 彻底搞懂HashMap,HashTable,ConcurrentHashMap之关联.
注: 今天看到的一篇讲hashMap,hashTable,concurrentHashMap很透彻的一篇文章, 感谢原作者的分享. 原文地址: http://blog.csdn.net/zhanger ...
随机推荐
- React当中的路由使用
React 当中的路由 使用React构建的单页面应用,要想实现页面间的跳转,首先想到的就是使用路由.在React中,常用的有两个包可以实现这个需求,那就是react-router和react-rou ...
- 管理用户和组 、 tar备份与恢复 、 cron计划任务-云计算学习(4)
配置用户和组账号 问题 本例要求创建下列用户.组以及组的成员关系: 新建用户 alex,其用户ID为3456,密码是flectrag 创建一个名为 adminuser 的组 创建一个名为 natash ...
- web 安全之页面解析的流程学习
0x00 任务内容: 理解域名解析的整个过程 理解 web 页面请求的整个流程,绘制流程图(nginx 处理的 11 个过程) 学习 http 协议中的字段及含义 学习 http 请求方法以及返回状态 ...
- 每天一点python:正则表达式中的findall方法
举例:使用findall获取所有匹配的正则表达式文本,然后逐一替换. #! python3 """ A regular expression example: find ...
- Scala 基础(五):Scala变量 (二) 数据类型
1 scala数据类型介绍 Scala 与 Java有着相同的数据类型,在Scala中数据类型都是对象,也就是说scala没有java中的原生类型 Scala数据类型分为两大类 AnyVal(值类型) ...
- Tableau如何嵌入HTML
原文地址:https://blog.csdn.net/read_you2011/article/details/81476108 作者:read_you2011 说明 作为领先的数据可视化工具,Tab ...
- npm\cnpm\yarn\tyarn 关于源和代理的问题
npm 是一个包管理器.Node.js 自带. cnpm 是 npm 的阿里版,用的阿里源. yarn 是另一个包管理器,不自带,需要另外装.可以单独装,也可以用 npm 装. tyarn 是 yar ...
- react : umi 引入 antd 踩坑
首先要明确一个问题. 不管是 antd 还是 dva 还是别的什么东西,他们都是 umi 的插件——只要这个项目是使用 umi 脚手架生成的. 所以第一步应该是 .umirc.js (config.j ...
- blog only to u
2020/7/16 属于我的第一个博客正式上线,以后我将在此更新我的笔记和我的知识积累还有我那阔爱的小仙女!
- Java Script 数组
数组:有许多变量的集合,它们的名称和数据类型都是一致的. 定义 操作(添加修改) Var arr=new Array(): Var arr=[ ]; //定义 Var arr1=[ 1,2,3, ...