Hashtable 与 ConcurrentHashMap 总结

  • Post author:
  • Post category:其他


上一篇简单介绍了众多map中的HashMap ,最后提及HashMap的最大缺点就是线程不安全。今天我们介绍两种线程安全的map容器,看它们是如何来解决线程安全的问题。

问题:HashMap为什么线程不安全?

jdk1.7时会出现环形链表导致死循环 –》元凶是在扩容时进行元素插入使用的是

头插法(在1.8后使用尾插法解决了这个问题)

jdk1.8时会出现元素覆盖的情况 –》当线程A,B同时进行put操作 当判断完hash碰撞之后假如此时链表为null 会直接赋值,但此时线程A时间片执行完挂起 ,线程B在时间片内完成赋值操作,之后线程A继续执行 会将B直接覆盖 (

可理解为链表头元素丢失了

接下来我们引来一个今天第一个不常见的主角 Hashtable 这个小写看着确实很别扭,为什么说它不常见呢,因为平时很少使用它。至于为什么很少使用,我们后续再说先扒扒它的结构

来让我们详细想想好想根HashMap一样呀! 这货应该也是数组+链表的结构

我们来看看它上面的put 和 get 方法 一看便知真想

这两个都是同步方法,使用了synchronized 关键字相当于锁住了Hashtable的对象,Hashtable 就是使用了这种排它锁粗暴的解决了线程安全问题。但是也带来了不支持并发操作的问题。

接下来我们来介绍另一个主角 ConcurrentHashMap 下简称(Cmap)

它也是线程安全的map容器,它比Hashtable强在哪里呢?

先说结论:它能支持并发操作。并且在1.8中 Hashtable 一直是链表结构,而我们的Cmap在满足链表长度后会转化为红黑树解决链表过长的问题。

那它为什么能支持并发呢?

(jdk1.7还没下载好 原谅我偷懒不贴图了)1.7时代jdk实现Cmap的方案是分段锁,何为分段锁呢,就是先做两次散列把数据分段之后将分段数据存入

特殊Hashtable(segment)的数组

因为每个Hashtable都支持自己段内的线程安全操作,所以可以支撑数组长度的并发操作。另外Cmap中使用了volatile关键字,保证了读取元素的时候各个线程可见。

1.8时代就采取了不同的方案,1.8中Cmap所采用的数据结构根我们之前介绍的HashMap一致。

那它是如何实现线程安全的呢 ? 让我们来看看

jdk1.8为什么着么做呢?

首先cas操作肯定要比锁要轻量级的多,而在同样使用synchronized的地方,1.7是锁住了segment整个Hashtable 而 1.8只是锁住了更细级别的链表。而且可以随着扩容增加并发量!

可见1.8的优化是多棒!!

总结就到这里欢迎大家指正问题 !!  与君共勉



版权声明:本文为crazytal原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。