那么要如何保证HashMap的线程安全呢? 方法有很多,比如使用Hashtable或者Collections.synchronizedMap,但是这两位选手都有一个共同的问题:性能。因为不管是读还是写操作,他们都会给整个集合上锁,导致同一时间的其他操作被阻塞。

虽然Hashtable和Collections.synchronizedMap解决了HashMap的线程不安全的问题,但是带来了运行效率不佳的问题。

基于以上所述,兼顾了线程安全和运行效率的ConcurrentHashMap就出现了。

在了解了HashMap之后,接下来就开始了解一下ConcurrentHashMap。
ConcurrentHashMap与HashMap相比,最关键的是要理解一个概念:segment
Segment其实就是一个Hashmap 。Segment也包含一个HashEntry数组,数组中的每一个HashEntry既是一个键值对,也是一个链表的头节点。
Segment对象在ConcurrentHashMap集合中有2的N次方个,共同保存在一个名为segments的数组当中。(类比HashMap来理解Segment就好)

因此ConcurrentHashMap的结构为:

换言之,ConcurrentHashMap是一个双层哈希表。在一个总的哈希表下面,有若干个子哈希表。(这样的双层结构,类似于数据库水平拆分来理解)

ConcurrentHashMap如此的设计,优势主要在于:
每个segment的读写是高度自治的,segment之间互不影响。这称之为“锁分段技术”;

看一下并发情况下的ConcurrentHashMap:
情景一:不同segment的并发写入


不同的Segment是可以并发执行put操作的

情景二:同一segment的并发写入

因为segment的写入是上锁的,因此对 同一segment的并发写入会被阻塞;

情景三:同一segment的一写一读

同一segment的写和读是可以并发执行的;


看到此处,就已经知道了ConcurrentHashMap的并发情况,有兴趣的话可以继续看下ConcurrentHashMap的具体读写过程。

Get方法:

1.为输入的Key做Hash运算,得到hash值。

2.通过hash值,定位到对应的Segment对象

3.再次通过hash值,定位到Segment当中数组的具体位置。

Put方法:

1.为输入的Key做Hash运算,得到hash值。

2.通过hash值,定位到对应的Segment对象

3.获取可重入锁

4.再次通过hash值,定位到Segment当中数组的具体位置。

5.插入或覆盖HashEntry对象。

6.释放锁。


看到此处,对于ConcurrentHashMap的Get和Put的过程(读写过程)就有了一个完整的了解了。
基于上述,会有一个问题:
每一个segment各自持有锁,那么在调用size()方法的时候(size()在实际开发大量使用),怎么保持一致性呢
详细描述一下上面问题的情景:
Size方法的目的是统计ConcurrentHashMap的总元素数量, 肯定要把每个segment内部的元素数量都加起来
那么假设一种情况,在统计segment元素数量的过程中,在统计结束前,已统计过的segment插入了新的元素,size()返回的数量就会出现不一致的问题。
为解决这个问题,ConcurrentHashMap的Size()方法是通过一个嵌套循环解决的,大体过程如下:
1.遍历所有的Segment。

2.把Segment的元素数量累加起来。

3.把Segment的修改次数累加起来。

4.判断所有Segment的总修改次数是否大于上一次的总修改次数。如果大于,说明统计过程中有修改,重新统计,尝试次数+1;如果不是。说明没有修改,统计结束。

5.如果尝试次数超过阈值,则对每一个Segment加锁,再重新统计。

6.再次判断所有Segment的总修改次数是否大于上一次的总修改次数。由于已经加锁,次数一定和上次相等。

7.释放锁,统计结束。

这种解决办法是不是似曾相识?没错,这种思想和乐观锁悲观锁的思想如出一辙(不熟悉乐观锁的道友可以看我转的一篇非常生动的介绍,传送门)

为了不锁所有segment,首先乐观地假设size过程中不会有修改。当尝试一定次数,才无奈转悲观,锁住所有segment以保证一致性。

补充:
1、以上都是基于Java1.7的ConcurrentHashMap原理和代码;

2、ConcurrentHashMap在对Key求Hash值的时候进行了两次Hash,目的是为了实现Segment均匀分布。

小结


说了那么多,针对Map子类的安全性可以总结如下几点:

  • HashMap采用链地址法解决哈希冲突,多线程访问哈希表的位置并修改映射关系的时候,后执行的线程会覆盖先执行线程的修改,所以不是线程安全的
  • Hashtable采用synchronized关键字解决了并发访问的安全性问题但是效率较低
  • ConcurrentHashMap使用了线程锁分段技术,每次访问只允许一个线程修改哈希表的映射关系,所以是线程安全的

转载于:https://www.cnblogs.com/csong7876/p/9090779.html

面试之Hashtable和ConcurrentHashMap相关推荐

  1. 面试必备:HashMap、Hashtable、ConcurrentHashMap的原理与区别

    本文转载自 夏雪冬日:https://www.cnblogs.com/heyonggang/p/9112731.html 在实际面试过程中出现集合 Map 的概率接近 100%,可见不背上个 Map ...

  2. HashMap、HashTable、ConcurrentHashMap、HashSet区别 线程安全类

    HashMap专题:HashMap的实现原理--链表散列 HashTable专题:Hashtable数据存储结构-遍历规则,Hash类型的复杂度为啥都是O(1)-源码分析 Hash,Tree数据结构时 ...

  3. Java集合——HashMap、HashTable以及ConCurrentHashMap异同比较

    转发:https://www.cnblogs.com/zx-bob-123/archive/2017/12/26/8118074.html 0. 前言 HashMap和HashTable的区别一种比较 ...

  4. 对比分析HashMap,HashTable,ConcurrentHashMap,LinkedHashMap,LURLinkedHashMap(一)

    前言: 这次写几篇 关于 HashMap,HashTable,ConcurrentHashMap,LinkedHashMap,LURLinkedHashMap 源码分析. 如果直接将他们源码,并不好理 ...

  5. Java Main Differences between HashMap HashTable and ConcurrentHashMap

    转自这篇帖子:http://www.importnew.com/7010.html HashMap和Hashtable的比较是Java面试中的常见问题,用来考验程序员是否能够正确使用集合类以及是否可以 ...

  6. 3、HashMap、HashTable和ConcurrentHashMap的区别?

    HashMap和HashTable的区别一种比较简单的回答是: (1)HashMap是非线程安全的,HashTable是线程安全的. (2)HashMap的键和值都允许有null存在,而HashTab ...

  7. HashMap、HashTable和ConcurrentHashMap的区别?

    HashMap和HashTable的区别一种比较简单的回答是: (1)HashMap是非线程安全的,HashTable是线程安全的. (2)HashMap的键和值都允许有null存在,而HashTab ...

  8. Java经典面试题:HashMap和HashTable以及ConcurrentHashMap分析

    本文转载于:https://segmentfault.com/a/1190000038989327 前言: HashMap 应该算是 Java 后端工程师面试的必问题,因为其中的知识点太多,很适合用来 ...

  9. HashMap、HashTable和ConcurrentHashMap的区别

    HahMap.HashTable和ConcurrentHashMap的区别: HashMap HashTable ConcurrentHashMap null键 允许 不允许 不允许 null值 允许 ...

最新文章

  1. 烂泥:【解决】VMware Workstation中安装ESXI5.0双网卡问题
  2. java aop注解拦截_Spring AOP 拦截指定注解标识的类或方法
  3. ubuntu-桌面菜单栏、任务栏、标题栏都不见了-解决办法
  4. 调用阿里API获取城市天气信息
  5. 2021年江西省研究生数学建模竞赛题目(二)题目:全国人口普查问题
  6. 天空卫士API数据安全解决方案
  7. 云服务器 架设传奇_传奇私服架设教程
  8. 谷歌浏览器访问抖音网页版白屏
  9. 公众号附件链接怎么放?
  10. stroage——SAN存储与WINDOWS主机连接
  11. VS:如何解决VS2015的30天试用期已过即VS2015许可证已过期的问题
  12. 创建一个商品类Product类,在该类中定义3个属性id,name,price和重写toString()方法
  13. java类图_java UML类图的使用-UML基础-火龙果软件工程
  14. Android高级进阶——绘图篇(五)setXfermode 设置混合模式
  15. 易宝支付Demo,生产中封装成简洁的代付接口,不用request如何获取项目运行时的真实路径...
  16. 学组合数学心得与题解(一)——组合计数
  17. Meteinfo结合Arcgis制作风矢量图
  18. 动态修改RelativeLayout的宽高
  19. 移动div盒子 原生js
  20. 【Java代码】两个数组拼接成一个数组

热门文章

  1. 柯南君:看大数据时代下的IT架构(5)消息队列之RabbitMQ--案例(Work Queues起航)...
  2. 为云服务立规矩——首批可信云服务认证名单公布
  3. Swift 中使用 SQLite——修改和删除数据
  4. React组件设计之边界划分原则
  5. django模板的导入
  6. 【bzoj1251】序列终结者(伸展树)
  7. 机器学习(三)--- scala学习笔记
  8. How to check Laravel version?
  9. core Animation之CAKeyframeAnimation(关键帧动画)
  10. VirtualBox: Effective UID is not root