背景知识:我们知道
HashMap是线程不安全的,多线程情况下,进行put操作会引起死循环,导致CPU利用率比较高,所以在并发情况下不能使用HashMap;而
Hashtable是线程安全的,其实就是在put操作的时候加了syncronized, 但是当一个线程在进行put操作时,其它的线程连get操作也不能进行,导致阻塞或者轮询状态,所以竞争越来越激烈,效率低下。
目录结构:
1 | public class ConcurrentHashMap<K,V> extends AbstractMap<K,V> |
锁分段技术
Hashtable在多并发的情况下效率低下,是因为很多线程争夺同一把锁,而假如容器内有多把锁,每一把锁用于锁容器内的一部分数据,当多线程访问容器内不同段的数据时,线程间就不会存在锁竞争,从而有效的提高并发访问效率。
Segment
英文翻译:” 段“
内部结构是:二级的哈希表,如下图:

多个Segment 保存在一个名为segments 数组当中,每个 Segment 高度自治,各个 Segment 之前读写互不影响。
读写操作
get想要获取里面的元素,按照结构,我们大概也能猜测出如何获取。
- 首先根据
key计算的hash值; - 计算得出 在
segments数组中的下标,根据下标得出相对应的Segment; - 再根据
key的hash值,找到Segment数组中的位置
- 首先根据
putput 元素
- 1.首先根据
key计算hash值 - 2.通过
hash值,定位到具体的Segment对象 - 3.获取可重入锁
- 4.再次通过
hash值,定位到Segment数组中的具体位置 - 5.插入或覆盖原
HashEntry对象 - 6.释放锁
- 1.首先根据
统计Size大小
jdk 1.7:
1 | public int size() { |
- 遍历所有的
Segment - 把
Segment元素数量累加起来 - 把
Segment修改次数累加起来 - 判断所有的
Segment的总修改次数是否大于上一次的总修改次数。如果大于,说明统计过程有修改,尝试统计,统计次数+1;反之,统计结束。 - 如果统计次数超过阀值,则对每个
Segment加锁,重新统计 - 再次判断总修改次数是否大于上一次的总修改次数,
- 释放锁,统计结束。
- 遍历所有的