> 文章列表 > ConcurrentHashMap分段锁

ConcurrentHashMap分段锁

ConcurrentHashMap分段锁

1.分段锁的设计目的

ConcurrentHashMap 是支持高并发的线程安全的 HashMap。相较于 HashTable 使用 synchronized 方法来保证线程安全,ConcurrentHashMap 采用分段锁的方式,在线程竞争激烈的情况下 ConcurrentHashMap 的效率高很多。

ConcurrentHashMap 中的分段锁称为 Segment,它的内部结构是维护一个 HashEntry 数组,同时 Segment 还继承了 ReentrantLock。

当需要 put 元素的时候,并不是对整个 ConcurrentHashMap 进行加锁,而是先通过 hashcode 来判断它放在哪一个分段中,然后对该分段进行加锁。所以当多线程 put 的时候,只要不是放在同一个分段中,就可以实现并行的插入。分段锁的设计目的就是为了细化锁的粒度,从而提高并发能力。

ConcurrentHashMap 数据结构模型

ConcurrentHashMap 中维护着一个 Segment 数组,Segment 中有维护着一个 HashEntry 的数组,所以 ConcurrentHashMap 的底层数据结构可以理解为:数组 + 数组 + 链表

ConcurrentHashMap 分段锁的实现

ConcurrentHashMap 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程操作不同的分段,就不会存在锁竞争,提高并发访问率。

Segment 本身就继承了 ReentrantLock 具备了锁的功能,在每次 put 前都会先尝试 tryLock() 加锁,如果成功则进行元素存储;如果失败,就会调用 Segment 的 scanAndLockForPut() 尝试循环加锁并扫描指定的 key。

put的完整流程

3.JDK1.8中的ConcurrentHashMap

jdk1.8 中的 ConcurrentHashMap 中废弃了 Segment 锁,直接使用了数组元素,数组中的每个元素都可以作为一个锁。在元素中没有值的情况下,可以直接通过 CAS 操作来设值,同时保证并发安全;如果元素里面已经存在值的话,那么就使用 synchronized 关键字对元素加锁,再进行之后的 hash 冲突处理。

jdk1.8 的 ConcurrentHashMap 加锁粒度比 jdk1.7 里的 Segment 来加锁粒度更细,并发性能更好。