ConcurrentHashMap源码浅析

什么是 ConcurrentHashMap?

       ConcurrentHashMap是一种线程安全高效的Hashmap,因为hashmap不是线程安全的,在并发执行put时,可能会导致链表形成环形结构,于是entrynext节点永远不为空,因此陷入死循环

       而线程安全的hashTable则效率低下,原因是他使用synchronized来实现线程安全的,比如当一个线程使用容器put时,其他的线程会阻塞,不仅不能put,连get也不行,因此效率较低。

       而concureenthashmap使用了锁分段技术,hashtable效率低的根本原因是所有线程只竞争一把锁,而如果而如果每把锁只锁住容器里面的一部分数据,那么访问不同的数据时就不会存在锁的竞争问题,这就是锁分段技术,也因此可以提高效率。


ConcurentHashMap的结构

       和HashMap的结构类似,都是数组加链表的结构。首先ConcurentHashMap下面包含了一个Segment数组,每个Segment元素都是一个ReenTrantLock(可重入锁),它会锁住一部分的数据。每个Segment下面都是一个HashEntry数组,数组里面的每个元素都是一个链表类型的数据。

       也就是说,如果要访问某个元素,首先需要找到对应的Segment打开锁,然后在里面找到HashEntry的索引,再到下面的链表里面找。

结构图:

 


 

然后是各种底层的源码分析(主要针对ConcurrentHashMap的初始化、Segment的定位,以及get,put、size操作):

一下是对《并发编程的艺术》一书上的源码的理解:

1、初始化Segment数组

为了能通过按位与的散列算法来定位Segment数组的索引,因此会把Segment数组的大小利用不断地左移,来定成2的N次方。

 

2、初始化每个Segment

主要完成的工作就是初始化Segment里面的HashEntry数组,根据需要的总容量,来确定每个Segment里面的HashEntry数组长度,此外,还要计算HashEntry数组扩容的门槛值(阈值)(默认为数组长度的0.75)。

 

3、如何定位Segment

当后面要插入元素之前,首先要计算出元素需要分配在哪一个Segment里面,ConcurrentHashMap的散列算法比较特殊,他会对元素的HashCode进行一次再散列,目的是为了减少哈希冲突的可能性。

 

4、get操作

根据3的说法,想获得某个key对应的元素时,它会在key.hashcode()的基础上进行一次再散列,然后拿这个再散列的值先定位到Segment,然后再定位到HashEntry。

    需要注意的是,ConcurrentHashMap的get特别高效,因为整个get过程不需要加锁。我们知道HashTable的同步是通过加锁来完成的。因为它将共享变量都定义成了volatile类型,因此共享变量对所有线程可见,他修改之后,所有线程立即就会得到通知。因此get根本需要任何的上锁和解锁,非常快。

5、put操作

因为get只需要读,不需要写,因此他可以使用volatile。但是put需要写,因此他还是需要加锁的。执行put方法时,首先它会定位到Segment,然后先判断HashEntry数组容量是否超过初始化时定义的阈值,如果超过了就要对数组扩容。这里需要注意的是他比HashMap改进的一点是:在放元素之前判断,而不是HashMap的放元素之后判断,因为放完之后可能再也没有放新的元素,导致不必要的扩容。

扩容的时候,会创建一个容量是原来两倍的数组,然后将原来的元素进行再散列放到新数组里面去。

6、size操作

每个Segment里面有一个count变量,也是volitie类,用来保存当前Segment里面元素总数,但是并不能简单的吧所有的count求和,因为可能求和的过程中某个count改变了。也不是直接采用把所有写操作(put,remove,clean)加锁锁住防止改变,这样效率也比较低。

实际上是:分别尝试两次累加求和,如果这两次容器没有发生写操作,那么就直接输出,如果发生了,则加锁来解决。如何判断容器有没有发生写操作,通过元素的一个变量modCount,它会统计容器写操作的次数,每次发生put,remove,clean时,这个变量+1,通过判断两次求和之后modCount有没有变化来看容器有没有写操作。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值