什么是ConcurrentHashMap?不同JDK下ConcurrentHashMap的区别?
一、HashMap线程安全
我们知道,在并发情况下,使用HashMap会有线程安全的问题,那么如何避免呢?
想要避免Hashmap的线程安全问题有很多办法,比如改用HashTable或者Collections.synchronizedMap
但是,这两者有着共同的问题:性能。无论读操作还是写操作,他们都会给整个集合加锁,导致同一时间的其他操作为之阻塞。
在并发环境下,如何能够兼顾线程安全和运行效率呢?这时候ConcurrentHashmap就应运而生来。
二、ConcurrentHashMap
ConcurrentHashMap优势就是采用了[锁分段技术],每一个Segment就好比自治区,读写操作高度自治,Segment之间互不影响。
1. Segment
这里面涉及到一个比较关键的概念:Segment。
Segment本身就相当于一个HashMap对象。同HashMap一样,Segment包含一个HashEntry数组,数组中的每一个HashEntry既是一个键值对,也是一个链表的头节点。
单一的Segment结构如下:
像这样的Segment对象,在ConcurrentHashMap集合中有2的N次方个,共同保存在一个名为segments的数组当中。
因此整个ConcurrentHashMap的结构如下:
可以说,ConcurrentHashMap是一个二级哈希表。在一个总的哈希表下面,有若干个子哈希表。
这样的二级结构,和数据库的水平拆分有些相似。
2. ConcurrentHashMap并发读写的几种情形
1)Case1: 不同Segment的并发写入
说明:不同Segment的写入是可以并发执行的。
2)Case2: 同一Segment的一写一读
说明:同一Segment的写和读是可以并发执行的。
3)Case3:同一Segment的并发写入
说明:Segment的写入是需要上锁的,因此对同一Segment的并发写入会被阻塞。
由此可见,ConcurrentHashMap当中每个Segment各自持有一把锁。在保证线程安全的同时降低了锁的粒度,让并发操作效率更高。
3. ConcurrentHashMap读写的详细过程
1)Get方法
- 为输入的Key做Hash运算,得到hash值。
- 通过hash值,定位到对应的Segment对象
- 再次通过hash值,定位到Segment当中数组的具体位置。
2)Put方法
- 为输入的Key做Hash运算,得到hash值。
- 通过hash值,定位到对应的Segment对象
- 获取可重入锁
- 再次通过hash值,定位到Segment当中数组的具体位置。
- 插入或覆盖HashEntry对象。
说明:从步骤可以看出,ConcurrentHashMap在读写时都需要二次定位。首先定位到Segment,之后定位到Segment内的具体数组下标。
4. 调用size方法时,如何解决一致性问题?
这个问题Key理解为:既然每一个Segment都各自加锁,那么在调用Size方法的时候,怎么解决一致性的问题呢?
分析:
Size方法的目的是统计ConcurrentHashMap的总元素数量, 自然需要把各个Segment内部的元素数量汇总起来。
但是,如果在统计Segment元素数量的过程中,已统计过的Segment瞬间插入新的元素,这时候该怎么办呢?
参考资料:《程序员小灰》微信公众号
标签:ConcurrentHashMap,hash,JDK,区别,并发,线程,数组,Segment From: https://www.cnblogs.com/hld123/p/18059376