ConcurrentHashMap的原理

news/2024/11/16 15:54:10/文章来源:https://www.cnblogs.com/xyuanzi/p/18352004

背景

我们知道hashmap是一个线程不安全的数据结构,在多线程编程的时候,多个线程同时向hashmap中put元素的时候,会发生数据丢失。多线程put操作后,再get操作导致死循环。
多线程put非NULL元素后,get操作得到NULL值。

使用

为了保证并发安全,我们使用hashmap的时候,建议是使用ConcurrentHashMap。

底层原理

1.7的时候,底层数据结构是大数组Segment(容量为16)和小数组HashEntry。默认是16个Segmement,每个HashEntry会存放一些键值对或者链表。
Segment继承了可重入锁,有加锁和释放锁的操作,这样就能保证多个线程访问ConcurrentHashMap的时候,同一时间只能有一个线程能够操作相应的节点,保证了线程安全。

性能相对于hashtable而言,效率提高了16倍。即当线程访问一个Segment 的时候,只对这一个Segment加锁,对于其他段的Segment,则可以继续被其他线程访问,不会有冲突。
1.8的时候,底层数据结构更新为数组+链表+红黑树。1.8不再有分段锁的设计,而是采用CAS和synchrionzed来保证并发安全。
CAS主要是用于put的过程中进行初始化,synchronzied主要是用于往map中插入元素的时候保证线程安全。
采用CAS自旋重试的方式进行初始化,是为了保证只有一个线程完成map的初始化问题,因为多个线程同时初始化,会产生数据丢失的问题。这边使用CAS原子操作,通过修改sizeCtl变量成功与否来代表是否抢占到了锁。如果抢占到了,由该线程完成map的初始化工作;
如果没抢占到,那就进行While循环,自旋重试,直到该map初始化成功,循环自动退出,自旋也随之结束。
初始化结束以后,对于一个put(key, val)操作,首先计算出该key的哈希值,从而得到该key在数组中的插入位置。
首先使用CAS的方式插入key-val, 先判断该位置是否为null, 为null, CAS插入元素。如果成功,则该key-val成功插入;
如果不为null,说明发生了碰撞,改用synchronized关键字,对头节点加锁,然后将key-val插入链表或者红黑树。
插入前,判断是链表还是红黑树,不同的结构处理方式不同;
如果是链表,那么从链表的头节点开始向下遍历,遍历的每个节点:
使用equlas判断key相等否,相等,则修改该key 的value; 否则,把当前的key value插入链表的最后一个节点。
如果是红黑树,那么putTreeVal完成值的存储。
要说明的是,这种锁控制在单个数据节点上,16位的数组可以支持16个线程并发写入数据。

源码

public V put(K key, V value) {return putVal(key, value, false);}/** Implementation for put and putIfAbsent */final V putVal(K key, V value, boolean onlyIfAbsent) {//1、数据检查if (key == null || value == null) throw new NullPointerException();//2、求key哈希int hash = spread(key.hashCode());int binCount = 0;  //记录遍历的节点数,可以用于判断是否要链表转化为红黑树for (Node<K,V>[] tab = table;;) {  //死循环Node<K,V> f; int n, i, fh;if (tab == null || (n = tab.length) == 0)  //检查 table 是否初始化tab = initTable();//使用哈希值计算索引 i 并检查该位置是否为空。如果为空,使用 CAS 操作插入新节点,并跳出循环。else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { if (casTabAt(tab, i, null,new Node<K,V>(hash, key, value, null)))break;                   // no lock when adding to empty bin}else if ((fh = f.hash) == MOVED) // MOVEDd标志用于判断是否已经节点迁移//当一个桶(bin)中的所有节点都被迁移到新的数组中后,原来的位置上会放置一个特殊的转发节点,表示这个桶已经处理完毕。此时,转发节点的 hash 字段会被设置为 MOVED(即 -1)。tab = helpTransfer(tab, f);//协助迁移else {   //如果碰撞了  需要使用synchronized,放弃cas,f是table那个碰撞节点V oldVal = null;synchronized (f) {if (tabAt(tab, i) == f) { // 经典的双重检查,防止当前线程获取table锁之前,tabAt(tab, i)被其它线程改变了if (fh >= 0) {// 哈希值>=0代表是链表,<0代表是红黑树binCount = 1;for (Node<K,V> e = f;; ++binCount) {K ek;if (e.hash == hash &&((ek = e.key) == key ||(ek != null && key.equals(ek)))) {  // 三比较,hashcode==hashcode,key==key,key.equals(key)oldVal = e.val;if (!onlyIfAbsent)e.val = value;break;}Node<K,V> pred = e;if ((e = e.next) == null) {pred.next = new Node<K,V>(hash, key,value, null);break;}}}else if (f instanceof TreeBin) { // 红黑树Node<K,V> p;binCount = 2;  //binCount 被初始化为 2,因为红黑树中的节点数计算方式不同于链表。 具体原因我不知道if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,value)) != null) {oldVal = p.val;if (!onlyIfAbsent)p.val = value;}}}}if (binCount != 0) {   // 判断是否要扩容 TREEIFY_THRESHOLD=8if (binCount >= TREEIFY_THRESHOLD)treeifyBin(tab, i);if (oldVal != null)return oldVal;break;}}}addCount(1L, binCount);  //计数 里面通过cas维护元素个数。return null;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/780799.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ABC201E Xor Distances 题解

从洛谷搬过来的题解,因为感觉和上一把 ABC的E有点像呐ABC201E Xor Distances 题解 题目大意 给定一个带权树,求树上每两点的简单路径上的边权的异或和的和。 形式化的,定义 \(dis(i,j)\) 为 \(i\) 到 \(j\) 的简单路径上的边权的异或和,求 \(\large\sum\limits_{i=1}^n\sum…

学生Java学习路程-6

ok,到了一周一次的总结时刻,我大致会有下面几个方面的论述:1.这周学习了Java的那些东西2.这周遇到了什么苦难3.未来是否需要改进方法等几个方面阐述我的学习路程。 复习面向对象数组 数组的三种初始化方法:默认,静态,动态引用类型Man放入数组中的测试代码数组的拷贝 使用…

Lazysysadmin靶机笔记

Lazysysadmin靶机笔记 概述 lazysysadmin是一台Vulnhub靶机,整体比较简单,要对一些存在服务弱口令比较敏感。 靶机地址:https://pan.baidu.com/s/19nBjhMpGkdBDBFSnMEDfOg?pwd=heyj 提取码:heyj 一、nmap扫描 1、主机发现 # -sn只做ping扫描,不做端口扫描 sudo nmap -sn 1…

图片压缩保证让你看的明明白白

场景 很多时候,都会遇见图片上传的场景。 在上传给服务器之前。 前端为了节省服务器的存储空间。 会对图片进行压缩。 下面我们来一起学习一下图片压缩。 图片压缩的步骤: 1.选择图片。使用 <input type="file">来实现 2.将选择的图片显示出来。 获取到图片的…

学前准备工作

什么是计算机computer:全称电子计算机,简称电脑。 能够按照程序运行,自动、高速处理海量数据的现代化智能电子设备 由软件和硬件组成 常见形式有台式计算机,笔记本计算机,大型计算机等 广泛应用在:科学计算、数据处理、自动控制、计算机辅助设计、人工智能等领域。计算机…

多元时间序列分析统计学基础:基本概念、VMA、VAR和VARMA

多元时间序列是一个在大学课程中经常未被提及的话题。但是现实世界的数据通常具有多个维度,所以需要多元时间序列分析技术。在这文章我们将通过可视化和Python实现来学习多元时间序列概念。这里假设读者已经了解单变量时间序列分析。 1、什么是多元时间序列? 顾名思义,多元时…

wqs二分

wqs二分 用来处理一类带有限制的问题,如恰好选 \(k\) 个,本质是通过二分来规避这个选取数量的限制。 使用前提:原问题具有凹凸性。设 \(g_i\) 表示选 \(i\) 个物品的答案,那么所有 \((i, g_i)\) 点组成一个凸包,满足 \(g(k)\) 单调。 这类题目通常有以下特点:如果不限制选…

IDEA Sonar 扫描

1. 修改SonarQube-7.7\conf\sonar.properties数据库配置2. 启动SonarQube-7.7\bin\windows-x86-64\StartSonar.bat,打开 localhost:9000,账密 admin / admin3. pom文件配置:<profiles><profile><id>sonar</id><properties><sonar.host.url…

[AGC052B] Tree Edges XOR

好题,可以直接作为套路记录一下。 [AGC052B] Tree Edges XOR 题目大意: 给你一棵树,有奇数个点,每个边有边权 \(w_i\)。每次你可以选出一条边,将和这条边的所有相邻的边都异或这条边的边权,问你能否得到最终状态(操作次数不定)。 思路: 首先,上来会发现每次操作影响的…

[JVM] 应用诊断工具之java命令

0 序本章对java命令的使用、最佳实践进行全方位的总结。1 java命令 1.0 场景:查看版本方法1# java -version java version "1.8.0_261" Java(TM) SE Runtime Environment (build 1.8.0_261-b12) Java HotSpot(TM) 64-Bit Server VM (build 25.261-b12, mixed mode)方…

洛谷 P1127 词链——题解

洛谷P1127题解传送锚点摸鱼环节 词链 题目描述 如果单词 \(X\) 的末字母与单词 \(Y\) 的首字母相同,则 \(X\) 与 \(Y\) 可以相连成 \(X.Y\)。(注意:\(X\)、\(Y\) 之间是英文的句号 .)。例如,单词 dog 与单词 gopher,则 dog 与 gopher 可以相连成 dog.gopher。 另外还有一…