概述

我们知道, 在 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. 基于Hexo的博客管理恢复

    若重装电脑或更换电脑后 该如何恢复博客的管理? 1.确保之前博客源代码文件夹及文件保存在公库或私库中 例如: 我这里采用的是闭源存放方案,故为私库 这是源码文件样式 2.在新电脑上重新安装git,no ...

  2. 关于CSP-S2019的一篇游记

    怎么讲呢? Day1:7:00左右从家里出发,准备还是做得比较充分,早饭也记得吃了.路上闭目养神了一会儿,7:50左右到了大门附近,和大家再次把一些自认为还是比较重要的数据结构之类的再复习了一下.进去 ...

  3. YOLOv5模型训练及检测

    一.为什么使用YOLOv5 二.软件工具 2.1 Anaconda https://www.anaconda.com/products/individual 2.2 PyCharm https://w ...

  4. 基于GDAL库海洋表温日平均计算工具设计与实现 C++版

    技术背景 在对物理海洋数据处理过程中,表层温度是众多要素中的一种,本文书要是针对海洋表温数据批量日平均处理的一个工具设计.首先要在对当前的SST数据文件作一下简要的说明,SST全称为sea surfe ...

  5. 四探循环依赖 → 当循环依赖遇上 BeanPostProcessor,爱情可能就产生了!

    开心一刻 那天知道她结婚了,我整整一个晚上没睡觉,开了三百公里的车来到她家楼下,缓缓的抽了一支烟...... 天渐渐凉了,响起了鞭炮声,迎亲车队到了,那天披着婚纱的她很美,真的很美! 我跟着迎亲车队开 ...

  6. Dubbo源码剖析六之SPI扩展点的实现之Adaptive功能实现原理

    接Dubbo源码剖析六之SPI扩展点的实现之getExtensionLoader - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)继续分析Adaptive功能实现原理.Adaptive的主 ...

  7. Welcom to my studyspace! 欢迎来到我的学习空间!

    由于新手,博客才刚开始起步,用于记录我的学习,博客的建设后续需要将其完善 对我的博客进行diy 用于分享我的学习经历,一些笔记,还有一些小小的心得

  8. 使用Supervisord部署go应用

    nohup nohup 是后台作业的意思, nohup运行的进程将会忽略终端信号运行.即后台运行一个命令.nohup COMMAND &用nohup运行命令可以使命令永久的执行下去,和用户终端 ...

  9. gulp更新4.0后的报错(gulp报Did you forget to signal async completion?)

    本文首发于青云工作室 原文链接为 https://qystudio.ltd/posts/55153.html 缘起 今天我升级了gulp到4.0,在git三件套之后,网站并没有更新,我便登录了gith ...

  10. java 接口 文件传输

    调用接收端 @ApiOperation(value = "文件请求展示方法") @RequestMapping(value = "/showFile", met ...