HashMap详解(三)

上一篇分析的扩容是基于JDK1.8,当发生哈希碰撞的时候采用尾插法,并会动态决定用红黑树和链表来串,但是我们还是有必要了解一下,简单来说:
HashMap在多线程操作的时候,由于碰撞的时候链表采用头插法,线程A执行put的时候发现需要扩容,走到一半挂起,线程B也去put发现需要扩容,则resize,rehash一顿操作并把位置放好了,此时A线程拿到时间片操作也是一顿操作,此时出现同一index的第一个和第二个键值对相互指向,形成循环链表,此时get(第一个key)和get(第二个key)其实也都是没问题的,直到get(key)某一个key的时候散列到当前的index 并且不存在当前index上的链表上,此时出现死循环。
于是出现了ConcurrentHashMap

ConcurrentHashMap(JDK1.7)

HashMap详解(三)_第1张图片
如图所示,是由 Segment 数组、HashEntry 数组组成,和 HashMap 一样,仍然是数组加链表。

HashEntry和Entry唯一的区别就是其中的核心数据如 value 、next都是 volatile 修饰的,保证了获取时的可见性。

原理上来说:ConcurrentHashMap 采用了分段锁技术,其中 Segment 继承于 ReentrantLock。不会像 HashTable 那样不管是 put 还是 get 操作都需要做同步处理,理论上 ConcurrentHashMap 支持 CurrencyLevel (Segment 数组数量)的线程并发。每当一个线程占用锁访问一个 Segment 时,不会影响到其他的 Segment。

ConcurrentHashMap(JDK1.8)

HashMap详解(三)_第2张图片
看起来是不是和 1.8 HashMap 结构类似?

其中抛弃了原有的 Segment 分段锁,而采用了 CAS + synchronized 来保证并发安全性
put方法可以参照上一篇文章,另外HashEntry也被替换成了Node,但作用相同,get的时候都是通过内部成员变量保证获取最新的,所以像put一样加锁,提高了效率。

你可能感兴趣的:(计算机基础)