HashMapConcurrentHashMapHashTable

Posted Putarmor

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HashMapConcurrentHashMapHashTable相关的知识,希望对你有一定的参考价值。

三者都是Map结构,具体有什么不同呢?今天给你整的明明白白

HashMap

HashMap底层基于数组+链表实现,当数组长度大于64且链表长度大于8时,就会变成数组+链表+红黑树。它为线程不安全的容器,在jdk1.7时HashMap存在死循环问题,jdk1.8时存在数据覆盖。

jdk1.7死循环分析

HashMap中存在负载因子,设定的值为0.75;当实际长度大于0.75*数组容量时,进行扩容;扩容时要把原来位置的数据映射到扩容后数组的位置上。

为什么设定一个负载因子(或扩容因子)?这是为了尽量的避免哈希冲突带来性能消耗,是采用空间换时间的方案。

当实际长度大于0.75倍数组容量时,进行扩容:在这里插入图片描述
先看resize中代码,我们重点关注的是transfer方法:
在这里插入图片描述
transfer底层源码:
在这里插入图片描述
首先给一个数组+链表:
在这里插入图片描述
经过一次循环后:
在这里插入图片描述
三次循环后映射到新数组的结果:
在这里插入图片描述
jdk1.7时候我们可以看到扩容之后,链表结点在新数组中的位置发生了颠倒

那么让两个线程去执行会发生什么结果呢?
在这里插入图片描述
我们可以看到,发生线程不安全现象!

怎么解决HashMap线程不安全现象*: 使用ConcurrentHashMap!

ConcurrentHashMap

ConcurrentHashMap是线程安全的容器,jdk1.7时它使用了分段锁保证线程安全;把数组分成一些段,给每个段加锁 lock/unlock,这样保证了锁的粒度更细。假如有两个写入操作,二者可以并发操作,不用进行排队,使程序执行的性能更高。采用悲观锁机制,

jdk1.8时进行锁优化,读的时候不加锁,写的时候进行加锁,使用了大量的CAS、volatile,采用乐观锁机制。

HashTable

HashTable是线程安全的容器,保证线程安全方式比较粗暴;会直接给put方法整体加锁(synchronized),导致程序性能很低,一般情况下不会使用HashTable.


面试题:HashMap、ConcurreentHashMap、HashTable区别是什么?

1.HashMap是非线程安全容器,jdk1.7时会出现死循环 ,jdk1.8时会出现数据覆盖;ConcurreentHashMap和HashTable都是线程安全的容器;
2.HashTable实现线程安全手段比较简单,它给put方法整体synchronized加锁,性能不高;ConcurreentHashMap是多线程下对HashMap的替代方案,jdk1.7时使用Lock分段加锁去保证线程安全;而在jdk1.8时使用CAS和volatile去实现线程安全,对锁进行优化,读的时候不加锁(读的时候数据可能不是最新的,因为读写操作可以同时进行),写的时候加锁。

以上是关于HashMapConcurrentHashMapHashTable的主要内容,如果未能解决你的问题,请参考以下文章