ConcurrentHashMap是如何保证线程安全的

news/2024/12/4 19:26:33/

ConcurrentHashMap是如何保证线程安全的

  • 定义和问题解决
  • JDK 1.7实现原理
  • JDK 1.8性能优化
  • 总结

定义和问题解决

ConcurrentHashMap相当于HashMap的多线程版本。

它的功能本质上和HashMap没有什么区别,因为HashMap在并发操作的时候会出现各种问题,比如:

  1. 死循环问题
  2. 数据覆盖等问题

而这些问题只要使用ConcurrentHashMap就能得到完美的解决。

ConcurrentHashMap是如何保证线程安全的。

JDK 1.7实现原理

在这里插入图片描述

JDK 1.7中ConcurrentHashMap的底层结构基本延续了HashMap的基本设计。它采用的是数组加链表的形式。

和HashMap不同的是ConcurrentHashMap中的数组被封分为大数组和小数组。大数组是Segment,小数组是HashEntry。大数组Segment可以理解为是一个数据库,而这个数据库又有很多张表,这个表就是HashEntry。

而每个HashEntry中又有很多条数据,这些数据采用的是链表结构

因为Segment本身是基于ReentrantLock重入锁的实现来加锁和释放锁的操作。这样的话就能够保证多线程同时访问ConcurrentHashMap的时候,同一时间只能有一个线程操作对应的节点。这样的话,保证了ConcurrentHashMap的线程安全。也就是说ConcurrentHashMap的线程安全是建立在Segment的加锁的基础上,所以我们称它为分段锁或者是分片锁

JDK 1.8性能优化

在JDK1.7中,ConcurrentHashMap虽然是线程安全的,但是它的底层实现是采用数组加链表的形式。所以在数据比较多的情况下,因为要遍历整个链表,这样的话会降低它的访问性能。

所以JDK1.8以后,就采用了数组加链表加红黑树的方式进行的优化。其具体实现如图所示:

在这里插入图片描述
当我们的链表长度大于8的时候,并且数组长度大于64的时候,链表就会升级为红黑树的结构。

JDK1.8中,ConcurrentHashMap保留了Segment的定义,但是者仅仅是为了保证序列化的时候的兼容性,不再有任何结构上的用途了。那在JDK1.8中,ConcurrentHashMap的源码又是如何实现的呢?

它主要是通过CAS加volatile或者是synchronized的方法来实现的,保证线程安全,我们可以从源码片段中看到添加元素的时候,首先会判断容器是否为空,如果容器为空,就会使用volatile加CAS来初始化。如果容器不为空,就会根据存储的元素计算该位置是否为空。如果根据存储元素的计算结果为空,就会利用CAS来设计该节点;如果根据存储元素的计算结果不为空,就会使用synchronized加锁来进行实现,然后去遍历桶中的数据,并且替换或新增节点到桶中。最后判断是否有必要转为红黑树。这样就保证了并发访问的线程安全。在这里插入图片描述

如果把上面的执行用一句话来归纳的话,就相当于ConcurrentHashMap通过对头节点加锁来保证线程安全,这样设计的好处是使得锁的粒度相比Segment来说更小了。发生hash冲突和加锁的频率也更低了,而在并发场景下操作性能也提高了。而且当数据量比较大的时候,查询性能也得到了进一步的提升。

总结

  1. JDK 1.7给Segment添加ReentrantLock锁来实现线程安全
    ConcurrentHashMap在JDK1.7中使用的是数组加链表结构,其中数组分为两大类,大数组是Segment,小数组是HashEntry。而加锁是通过Segment添加ReentrantLock重入锁来保证线程安全的。
  2. JDK 1.8通过CAS或者synchronized来实现线程安全
    ConcurrentHashMap在JDK1.8中使用的是数组加链表加红黑树的方式来实现。它是通过CAS或者synchronized来实现线程安全。并且也缩小了锁的粒度。查询性能也到了进一步的提升。

参考资料:【Java面试】京东二面,ConcurrentHashMap是如何保证线程安全?


http://www.ppmy.cn/news/50772.html

相关文章

Spring Boot的基础使用和< artifactId>spring-boot-maven-plugin</ artifactId>爆红的处理

Spring Boot的基础使用和< artifactId>spring-boot-maven-plugin</ artifactId>爆红的处理 Spring Boot概述 微服务概述 微服务Microservices是一种软件架构风格&#xff0c;他是以专注于单一责任与功能的小型功能区块Small Building Blocks 为基础&#xff0c;…

SpringBoot 循环依赖,如何解决?

什么是循环依赖? 循环依赖是指在Spring Boot 应用程序中,两个或多个类之间存在彼此依赖的情况,形成一个循环依赖链。 在这种情况下,当一个类在初始化时需要另一个类的实例,而另一个类又需要第一个类的实例时,就会出现循环依赖问题。这会导致应用程序无法正确地初始化和…

58 openEuler搭建Mariadb数据库服务器-管理数据库

文章目录 58 openEuler搭建Mariadb数据库服务器-管理数据库58.1 创建数据库58.2 查看数据库58.3 选择数据库58.4 删除数据库58.5 备份数据库58.6 恢复数据库 58 openEuler搭建Mariadb数据库服务器-管理数据库 58.1 创建数据库 可以使用CREATE DATABASE语句来创建数据库。 CR…

【Git 入门教程】第一节、什么是Git?

在软件开发中&#xff0c;代码的管理和版本控制非常重要。为了更好地管理代码&#xff0c;需要使用一种有效的工具来保证代码的质量和稳定性。而Git正是这样一种工具。 一、概念 Git是一种分布式版本控制系统&#xff0c;它可以追踪文件的变化&#xff0c;并且可以协同工作。它…

软件测试?月薪20k+?不会自动化测试的我真的很难....

做自动化测试后悔吗&#xff1f; 后悔&#xff0c;真的后悔&#xff01; 后悔没有早点学..... 虽然现在网上到处都在散播35的焦虑&#xff0c;姑且信之&#xff0c;那么反问你&#xff0c;如果你30岁了&#xff0c;那么给你5年&#xff0c;能够在某个领域成为专家呢&#xf…

5个方法,帮助你快速提高团队管理效率

团队中&#xff0c;大家看起来都很忙&#xff0c;但最终交付的结果却总是差强人意。会议那么多&#xff0c;但有效的却很少越管理&#xff0c;但偏偏有时候越管理越乱......相信以上这些问题&#xff0c;很多管理者都有遇到过&#xff0c;团队管理是一个项目中最关键的一环。好…

RocketMQ 5.0 时代,6 张图带你理解 Proxy!

大家好&#xff0c;我是君哥。今天来聊一聊 RocketMQ 5.0 中的 Proxy。 RocketMQ 5.0 为了更好地拥抱云原生&#xff0c;引入了无状态的 Proxy 模块&#xff0c;新的架构图如下&#xff1a; 引入 Proxy 模块后&#xff0c;Proxy 承担了协议适配、权限管理、消息管理等计算功能…

iview render函数(vue render函数)

iview 的render函数就是vue的render函数&#xff0c;iview常用在表格里面自定义内容&#xff0c;下面来看render函数常用的配置&#xff1a; 1、 h是createdElement的简写&#xff0c;有3个参数&#xff1a; 语法&#xff1a;render:(h,params)>{} render:(h,params) >…