[java] ConcurrentHashMap

在分析ConcurrentHashMap的实现原理之前,先来介绍一下hashmap以及hashtable的特点和可能存在的问题.

HashTable

hashTable,它是线程安全的,它在所有涉及到多线程操作的都加上了synchronized关键字来锁住整个table,这就意味着所有的线程都在竞争一把锁,在多线程的环境下,它是安全的,但是无疑是效率低下的。已经几乎被淘汰了。

Hashmap

首先hashmap是线程不安全的,即在多线程环境下使用会有问题。更新丢失是肯定会有的,最出名的问题是,HashMap在put的时候,插入的元素超过了容量(由负载因子决定)的范围就会触发扩容操作,就是rehash,这个会重新将原数组的内容重新hash到新的扩容数组中,在多线程的环境下,存在同时其他的元素也在进行put操作,如果hash值相同,可能出现同时在同一数组下用链表表示,造成闭环,导致在get时会出现死循环,所以HashMap是线程不安全的。

在jdk1.8之前,hashmap使用数组加链表的方式实现,链表用于处理hash冲突,即碰撞,在jdk1.8之中引入了红黑树来处理hash冲突,当链表长度超过8时,就转换为红黑树。HashMap处理“碰撞”增加了红黑树这种数据结构,当碰撞结点较少时,采用链表存储,当较大时(>8个),采用红黑树(特点是查询时间是O(logn))存储(有一个阀值控制,大于阀值(8个),将链表存储转换成红黑树存储)。

ConcurrentHashMap

在jdk1.7之中,ConcurrentHashMap使用的是分段锁技术实现,把HashMap分割成若干个Segment,在put的时候需要锁住Segment,get时候不加锁,使用volatile来保证可见性,当要统计全局时(比如size),首先会尝试多次计算modcount来确定,这几次尝试中,是否有其他线程进行了修改操作,如果没有,则直接返回size。如果有,则需要依次锁住所有的Segment来计算。

jdk7中ConcurrentHashmap中,当长度过长碰撞会很频繁,链表的增改删查操作都会消耗很长的时间,影响性能,所以jdk8 中完全重写了concurrentHashmap,代码量从原来的1000多行变成了 6000多 行,实现上也和原来的分段式存储有很大的区别

在jdk1.8之中,完全摒弃了segment的实现,而是借鉴了Hashmap的实现,也采用了数组加链表加红黑树的实现,并且也是在链表长度大于8时转换为红黑树。put时不是锁住segment而是锁住一个具体的node,减小锁粒度,提高了效率。

TreeMap

todo

猜你喜欢

转载自blog.csdn.net/topdeveloperr/article/details/80964273
今日推荐