java8的ConcurrentHashMap为何放弃分段锁

jdk1.7的分段锁

和hashmap一样,在jdk1.7中concurrentHashMap的底层数据结构是数组加链表.和hashMap不同的是ConcurrentHAshMap中存放的数据是一段段的,即由多个Segment(段组成的).每个segment中都有类似于数组加链表的结构

segment

ConcurrentHashMap有三个参数

  1. initialCapacity:初始容量,默认16
  2. loadFactor:加载因子,默认0.75
  3. concurrencyLevel:并发级别,默认16

其中并发级别控制了segment的个数,在一个ConcurrentHashMao创建后的Segment的个数是不能变的,扩容过程改变的是每个Segment的大小

concurrencyLevel,一经指定不可改变,如果后续concurrenHashMap的元素增加导致ConrruentHashMap需要扩容,ConcurrentHashMap不会增加Segment的数量,而只会增加Segment中链表数组的容量大小,这样的好处是扩容过程不需要对整个ConcurrentHashMap做rehash,而只需要对Segment里面的元素做一次rehash就可以了。

关于分段锁

Segment继承了重入锁ReentrantLock,有了锁的功能,每个控制的是一段,当每个Segment越来越大时,锁的粒度就变得有些大了

优势:分段锁的优势保证在操作不同段的map的时候可以并发执行,操作同段map的时候,进行锁的竞争和等待.着相对于直接对整个 map同步synchronized是有优势的.

缺点:缺点在于分成很多段,会比较浪费内存空间(不连续,碎片化)操作map时竞争同一个分段锁的概率是啥非常小的,分段锁反而会造成更新等操作时间的等待;当某个段很大时,分段锁性能会下降

jdk1.8

把数组中的每个元素看成一个桶。可以看到大部分都是CAS操作,加锁的部分是对桶的头节点进行加锁,锁粒度很小。

降低了synchronized的粒度(读不加锁,写对当前桶进行加锁)

为什么不用reentrantLock而用Synchronized

减少寸开销:如果使用ReentrantLock则需要节点来集成AQS来获得同步,增加内存开销,而1.8中只有头结点需要进行同步

synchronized是JVM直接支持的,JVM能够在运行时作出相应的优化措施:锁粗化、锁消除、锁自旋等等。

Last modification:April 19, 2022
如果觉得我的文章对你有用,请随意赞赏