概述

我们知道, 在 Java 5 之后,JDK 引入了 java.util.concurrent 并发包 ,其中最常用的就是 ConcurrentHashMap 了, 它的原理是引用了内部的 Segment ( ReentrantLock )  分段锁,保证在操作不同段 map 的时候, 可以并发执行, 操作同段 map 的时候,进行锁的竞争和等待。从而达到线程安全的目的, 且效率大于 synchronized。

但是在 Java 8 之后, JDK 却弃用了这个分段锁策略,接下来详细学习一下。

一、jdk1.7分段锁的实现

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

1.1 关于Segment

ConcurrentHashMap有3个参数:

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

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

1.2 关于分段锁

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

  • 分段锁的优势在于保证在操作不同段 map 的时候可以并发执行,操作同段 map 的时候,进行锁的竞争和等待。这相对于直接对整个map同步synchronized是有优势的。
  • 缺点在于分成很多段时会比较浪费内存空间(不连续,碎片化); 操作map时竞争同一个分段锁的概率非常小时,分段锁反而会造成更新等操作的长时间等待; 当某个段很大时,分段锁的性能会下降。

二、jdk1.8的map实现

和hashmap一样,jdk 1.8中ConcurrentHashmap采用的底层数据结构为数组+链表+红黑树的形式。数组可以扩容,链表可以转化为红黑树。

2.1 弃用原因

通过  JDK 的源码和官方文档看来, 他们认为的弃用分段锁的原因由以下几点:

  1. 加入多个分段锁浪费内存空间。
  2. 生产环境中, map 在放入时竞争同一个锁的概率非常小,分段锁反而会造成更新等操作的长时间等待。
  3. 为了提高 GC 的效率.

2.2 什么时候扩容?

  1. 当前容量超过阈值
  2. 当链表中元素个数超过默认设定(8个),当数组的大小还未超过64的时候,此时进行数组的扩容,如果超过则将链表转化成红黑树

2.3 什么时候链表转化为红黑树?

当数组大小已经超过64并且链表中的元素个数超过默认设定(8个)时,将链表转化为红黑树

ConcurrentHashMap的put操作代码如下:

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


三、为什么不用ReentrantLock而用synchronized ?

  • 减少内存开销:如果使用ReentrantLock则需要节点继承AQS来获得同步支持,增加内存开销,而1.8中只有头节点需要进行同步。
  • 内部优化:synchronized则是JVM直接支持的,JVM能够在运行时作出相应的优化措施:锁粗化、锁消除、锁自旋等等。

  参考资料

  • https://my.oschina.net/pingpangkuangmo/blog/817973

  • https://www.wanaright.com/2018/09/30/java10-concurrenthashmap-no-segment-lock/

  • https://cloud.tencent.com/developer/article/1509556

JDK8的 CHM 为何放弃分段锁的更多相关文章

  1. java8的ConcurrentHashMap为何放弃分段锁,为什么要使用CAS+Synchronized取代Segment+ReentrantLock

    原文地址:https://cloud.tencent.com/developer/article/1509556 推荐一篇 ConcurrentHashMap 和 HashMap 写的比较的的文章 j ...

  2. 分段锁——ConcurrentHashMap

    1.线程不安全的HashMap因为多线程环境下,使用Hashmap进行put操作会引起死循环,导致CPU利用率接近100%,所以在并发情况下不能使用HashMap. 2.效率低下的HashTable容 ...

  3. Java 中15种锁的介绍:公平锁,可重入锁,独享锁,互斥锁,乐观锁,分段锁,自旋锁等等

    Java 中15种锁的介绍 Java 中15种锁的介绍:公平锁,可重入锁,独享锁,互斥锁,乐观锁,分段锁,自旋锁等等,在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类 ...

  4. 学习ConcurrentHashMap1.7分段锁原理

    1. 概述 接上一篇 学习 ConcurrentHashMap1.8 并发写机制, 本文主要学习 Segment分段锁 的实现原理. 虽然 JDK1.7 在生产环境已逐渐被 JDK1.8 替代,然而一 ...

  5. redis分布式锁扣减库存弊端: 吞吐量低, 解决方法:使用 分段锁 分布式分段锁并发扣减库存--代码实现

    package tech.codestory.zookeeper.aalvcai.ConcurrentHashMapLock; import lombok.AllArgsConstructor; im ...

  6. Java中的各种锁--分类总结

    前言 本文需要具备一定的多线程基础才能更好的理解. 学习java多线程时,最头疼的知识点之一就是java中的锁了,什么互斥锁.排它锁.自旋锁.死锁.活锁等等,细分的话可以罗列出20种左右的锁,光是看着 ...

  7. 50道Java集合经典面试题(收藏版)

    前言 来了来了,50道Java集合面试题也来啦~ 已经上传github: https://github.com/whx123/JavaHome 1. Arraylist与LinkedList区别 可以 ...

  8. Java面试,面试题

    Java面试,面试题 HashMap,HashTable,ConcurrentHash的共同点和区别 HashMap HashTable ConcurrentHashMap ArrayList和Lin ...

  9. Java八股文纯享版——篇②:并发编程

    注: 1.笔记为个人归纳整理,尽力保证准确性,如有错误,恳请指正 2.写文不易,转载请注明出处 3.本文首发地址 https://blog.leapmie.com/archives/c02a6ed1/ ...

随机推荐

  1. IE,FF等浏览器兼容性问题

    _1:改变选中时候的背景色处理: ::selection { color:#fff; background-color: #5CB85C ;} ::-moz-selection { color:#ff ...

  2. js实现用按钮控制网页滚动、以及固定导航栏效果

    实现效果如下: 页面内有三个按钮,分别控制页面向上.向下移动,以及暂停,并设置有导航栏,在滚动到某一位置时显示.且当用户主动控制鼠标滑轮时,滚动效果自动关闭.本页面只是演示如何实现,进行了简单的布局, ...

  3. Spring中声明式事务的几个属性的解释

    声明式事务 @Transactional (通常用在service层)事务属性:传播行为,隔离级别,回滚,只读,过期 1,spring支持事务传播行为:propagation(常用以下两个)    ① ...

  4. IT职业技能图谱:架构师、H5、DBA、移动、大数据、运维...

    转载 作者:StuQ 文章收藏自微信:InfoQ   时隔近5个月,StuQ的小伙伴们再次出品了IT职业技能图谱更新版.这回除更新之前版本外,还添加了架构师.HTML 5.DBA等新的职业技能图谱.正 ...

  5. Note -「动态 DP」学习笔记

    目录 「CF 750E」New Year and Old Subsequence 「洛谷 P4719」「模板」"动态 DP" & 动态树分治 「洛谷 P6021」洪水 「S ...

  6. FutureTask类的get方法如何实现线程同步等待

    接上篇JDK中线程中实现同步等待闭环的一种方式 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com),为什么使用了FutureTask中的get方法就可以实现线程的同步等待?这就将重点讲述下F ...

  7. Spring Boot 自动配置(基本配置)

    在关于Spring Boot的第一篇中就提到了其几大特性,其中关于Java配置简化属性注入在上一篇SpringBoot外部配置 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)中已经介绍. ...

  8. 使用注解实现SpringIOC和SpringAOP

    使用注解实现ioc @Component:实现Bean组件的定义 @Repository:标注dao类 @Service:标注业务类 @Controller:标注控制类 Bean的自动装配: @Aut ...

  9. jsp页面获取请求参数问题记录

    同一个请求可以从请求路径中获取参数,使用param.参数名 window.location.href = "admin/page.html?pageNum="+pageNum+&q ...

  10. nginx加大缓存

    http { server { listen 0.0.0.0:81; server_name localhost; -- proxy_buffer_size 128k; proxy_buffers 3 ...