明企科技网站建设系统/哪个公司网站设计好
目录
原理:
初始化数据结构时的线程安全
put 操作时的线程安全
原理:
多段锁+cas+synchronize
初始化数据结构时的线程安全
在 JDK 1.8
中,初始化 ConcurrentHashMap
的时候这个 Node[]
数组是还未初始化的,会等到第一次 put()
方法调用时才初始化
final V putVal(K key, V value, boolean onlyIfAbsent) {if (key == null || value == null) throw new NullPointerException();int hash = spread(key.hashCode());int binCount = 0;for (Node<K,V>[] tab = table;;) {Node<K,V> f; int n, i, fh;// 判断Node数组为空if (tab == null || (n = tab.length) == 0)// 初始化Node数组tab = initTable();......
}
此时会有并发问题的,如果多个线程同时调用 initTable()
初始化 Node[]
数组怎么办?看看 Doug Lea
大师是如何处理的
private final Node<K,V>[] initTable() {Node<K,V>[] tab; int sc;// 每次循环都获取最新的Node[]数组引用while ((tab = table) == null || tab.length == 0) {// sizeCtl是一个标记位,若为-1,代表有线程在进行初始化工作了if ((sc = sizeCtl) < 0)// 让出CPU时间片Thread.yield(); // 此时,代表没有线程在进行初始化工作,CAS操作,将本实例的sizeCtl变量设置为-1 else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {// 如果CAS操作成功了,代表本线程将负责初始化工作try {// 再检查一遍数组是否为空if ((tab = table) == null || tab.length == 0) {// 在初始化ConcurrentHashMap时,sizeCtl代表数组大小,默认16// 所以此时n默认为16int n = (sc > 0) ? sc : DEFAULT_CAPACITY;@SuppressWarnings("unchecked")Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];// 将其赋值给table变量table = tab = nt;// 通过位运算,n减去n二进制右移2位,相当于乘以0.75// 例如16经过运算为12,与乘0.75一样,只不过位运算更快sc = n - (n >>> 2);}} finally {// 将计算后的sc(12)直接赋值给sizeCtl,表示达到12长度就扩容// 由于这里只会有一个线程在执行,直接赋值即可,没有线程安全问题,只需要保证可见性sizeCtl = sc;}break;}}return tab;
}
总结:就算有多个线程同时进行 put 操作,在初始化 Node[] 数组时,使用了 CAS 操作来决定到底是哪个线程有资格进行初始化,其他线程只能等待。用到的并发技巧如下
- volatile 修饰 sizeCtl 变量:它是一个标记位,用来告诉其他线程这个坑位有没有线程在进行初始化工作,其线程间的可见性由 volatile 保证
- CAS 操作:CAS 操作保证了设置 sizeCtl 标记位的原子性,保证了在多线程同时进行初始化 Node[] 数组时,只有一个线程能成功
put
操作时的线程安全
public V put(K key, V value) {return putVal(key, value, false);
}final V putVal(K key, V value, boolean onlyIfAbsent) {// K,V 都不能为空if (key == null || value == null) throw new NullPointerException();// 取得 key 的 hash 值int hash = spread(key.hashCode());// 用来计算在这个节点总共有多少个元素,用来控制扩容或者转换为树int binCount = 0;// 数组的遍历,自旋插入结点,直到成功for (Node<K,V>[] tab = table;;) { Node<K,V> f; int n, i, fh;// 当Node[]数组为空时,进行初始化if (tab == null || (n = tab.length) == 0) tab = initTable();// Unsafe类volatile的方式取出hashCode散列后通过与运算得出的Node[]数组下标值对应的Node对象// 此时 Node 位置若为 null,则表示还没有线程在此 Node 位置进行插入操作,说明本次操作是第一次else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {// 如果这个位置没有元素的话,则通过 CAS 的方式插入数据if (casTabAt(tab, i, null, // 创建一个 Node 添加到数组中,null 表示的是下一个节点为空new Node<K,V>(hash, key, value, null)))// 插入成功,退出循环 break; }// 如果检测到某个节点的 hash 值是 MOVED,则表示正在进行数组扩容 else if ((fh = f.hash) == MOVED) // 帮助扩容tab = helpTransfer(tab, f);// 此时,说明已经有线程对Node[]进行了插入操作,后面的插入很有可能会发生Hash冲突else {V oldVal = null;// ----------------synchronized----------------synchronized (f) {// 二次确认此Node对象还是原来的那一个if (tabAt(tab, i) == f) {// ----------------table[i]是链表结点----------------if (fh >= 0) {// 记录结点数,超过阈值后,需要转为红黑树,提高查找效率binCount = 1; // 遍历这个链表for (Node<K,V> e = f;; ++binCount) {K ek;// 要存的元素的 hash 值和 key 跟要存储的位置的节点的相同的时候,替换掉该节点的 value 即可if (e.hash == hash && ((ek = e.key) == key ||(ek != null && key.equals(ek)))) {oldVal = e.val;if (!onlyIfAbsent)e.val = value;break;}// 到了链表的最末端,将新值放到链表的最末端Node<K,V> pred = e;// 如果不是同样的 hash,同样的 key 的时候,则判断该节点的下一个节点是否为空if ((e = e.next) == null) { // ----------------“尾插法”插入新结点----------------pred.next = new Node<K,V>(hash, key,value, null);break;}}}// ----------------table[i]是红黑树结点----------------else if (f instanceof TreeBin) { Node<K,V> p;binCount = 2;// 调用putTreeVal方法,将该元素添加到树中去if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,value)) != null) {oldVal = p.val;if (!onlyIfAbsent)p.val = value;}}}}if (binCount != 0) {// 当在同一个节点的数目达到8个的时候,则扩张数组或将给节点的数据转为treeif (binCount >= TREEIFY_THRESHOLD)// 链表 -> 红黑树 转换treeifyBin(tab, i); // 表明本次put操作只是替换了旧值,不用更改计数值 if (oldVal != null)return oldVal;break;}}}addCount(1L, binCount);// 计数值加1return null;
}
总结:
put() 方法的核心思想:由于其减小了锁的粒度,若 Hash 完美不冲突的情况下,可同时支持 n 个线程同时 put 操作,n 为 Node 数组大小,在默认大小 16 下,可以支持最大同时 16 个线程无竞争同时操作且线程安全
当 Hash 冲突严重时,Node 链表越来越长,将导致严重的锁竞争,此时会进行扩容,将 Node 进行再散列,下面会介绍扩容的线程安全性。总结一下用到的并发技巧
- 减小锁粒度:将 Node 链表的头节点作为锁,若在默认大小 16 情况下,将有 16 把锁,大大减小了锁竞争(上下文切换),就像开头所说,将串行的部分最大化缩小,在理想情况下线程的 put 操作都为并行操作。同时直接锁住头节点,保证了线程安全
- 使用了 volatile 修饰 table 变量,并使用 Unsafe 的 getObjectVolatile() 方法拿到最新的 Node
- CAS 操作:如果上述拿到的最新的 Node 为 null,则说明还没有任何线程在此 Node 位置进行插入操作,说明本次操作是第一次
- synchronized 同步锁:如果此时拿到的最新的 Node 不为 null,则说明已经有线程在此 Node 位置进行了插入操作,此时就产生了 hash 冲突;此时的 synchronized 同步锁就起到了关键作用,防止在多线程的情况下发生数据覆盖(线程不安全),接着在 synchronized 同步锁的管理下按照相应的规则执行操作
参考:
【精选】ConcurrentHashMap是如何实现线程安全的_concurrenthashmap如何保证线程安全-CSDN博客
相关文章:

ConcurrentHashMap是如何实现线程安全的
目录 原理: 初始化数据结构时的线程安全 put 操作时的线程安全 原理: 多段锁cassynchronize 初始化数据结构时的线程安全 在 JDK 1.8 中,初始化 ConcurrentHashMap 的时候这个 Node[] 数组是还未初始化的,会等到第一次 put() 方…...

MYSQL:索引与锁表范围简述
一、聚簇索引原则 当有主键索引时,选择主键索引;如果没有主键索引,选择第一个的unique索引;如果都没有就选择隐藏生成的ROW_ID。 二、加锁原则 来自知乎MySQL探秘(七):InnoDB行锁算法 - 知乎 (zhihu.com) 在不通过索引条件查询时…...

15 款 PDF 编辑器帮助轻松编辑、合并PDF文档
PDF 编辑器在当今的数字环境中至关重要,因为 PDF 已成为共享和存储信息的首选格式。只需几分钟,可靠的 PDF 编辑器即可让用户能够根据其特定需求修改、定制和定制文档。在本文中,我们全面汇编了 15 款最佳免费 PDF 编辑器,让您可以…...

PS Raw中文增效工具Camera Raw 16
Camera Raw 16 for mac(PS Raw增效工具)的功能特色包括强大的图像调整工具。例如,它提供白平衡、曝光、对比度、饱和度等调整选项,帮助用户优化图像的色彩和细节。此外,Camera Raw 16的界面简洁易用,用户可…...

Flink源码解析二之执行计划⽣成
JobManager Leader 选举 首先flink会依据配置获取RecoveryMode,RecoveryMode一共两两种:STANDALONE和ZOOKEEPER。 如果用户配置的是STANDALONE,会直接去配置中获取JobManager的地址如果用户配置的是ZOOKEEPER,flink会首先尝试连接zookeeper,利用zookeeper的leadder选举服务发现…...

MySQL遍历所有表所有字段查找字符数据
MySQL遍历所有表所有字段查找字符数据 工作中有一些数据查找,但是在那个库那个表那个字段中并不明确,特别是敏感字符查找,如果数据量并不大,我们可以采用遍历整个库、表中字符来查找相关数据来解决该问题。 我们可以写一个存储过…...

Java中的异常处理机制是怎样的?
Java中的异常处理机制主要包括以下几个部分: 异常类(Exception Class):Java中的异常类继承自java.lang.Throwable,主要分为两大类:Error和Exception。Error表示程序无法处理的严重问题,如系统崩…...

高教社杯数模竞赛特辑论文篇-2023年A题:定日镜场的优化设计(附获奖论文及MATLAB代码实现)
目录 摘 要 一、 问题重述 1.1 问题背景 1.2 问题重述 二、 模型假设 三、 符号说明...

c语言实现http下载功能,显示进度条和下载速率
#include <stdio.h>//printf #include <string.h>//字符串处理 #include <sys/socket.h>//套接字 #include <arpa/inet.h>//ip地址处理 #include <fcntl.h>//open系统调用 #include <unistd.h>//write系统调用 #include <netdb.h>//…...
Educational Codeforces Round 157 (Rated for Div. 2) D. XOR Construction (思维题)
题目 给定长为n-1(n<2e5)的整数序列a,第i个数a[i](0<a[i]<2n) 构造一个长为n的整数序列b,满足: 1. 0到n-1在b数组中每个数恰好出现一次 2. 对于, 题目保证一定有解,有多组时可以输出任意一组 思路来源 …...

【unity实战】实现类似英雄联盟的buff系统
文章目录 先来看看最终效果前言开始BUFF系统加几个BUFF测试1. 逐层消失,升级不重置剩余时间的BUFF2. 一次性全部消失,升级重置剩余时间的BUFF3. 永久BUFF,类似被动BUFF4. 负面BUFF,根据当前BUFF等级计算每秒收到伤害值,…...

【C语言基础教程】函数指针与指针大小
文章目录 前言一、函数指针1.1 函数指针的概念1.2 三个示例代码示例1: 使用函数指针调用不同的函数示例 2: 使用函数指针实现回调函数示例 3: 使用函数指针数组 二、指针的大小2.1 前述2.2 指针大小如何决定?两方面理解 总结 前言 在C语言中,指针是一项…...

Web前端—网页制作(以“学成在线”为例)
版本说明 当前版本号[20231105]。 版本修改说明20231105初版 目录 文章目录 版本说明目录day07-学成在线01-项目目录02-版心居中03-布局思路04-header区域-整体布局HTML结构CSS样式 05-header区域-logo06-header区域-导航HTML结构CSS样式 07-header区域-搜索布局HTML结构CSS…...

Hive【Hive(八)自定义函数】
自定义函数用的最多的是单行函数,所以这里只介绍自定义单行函数。 Coding 导入依赖 <dependency><groupId>org.apache.hive</groupId><artifactId>hive-exec</artifactId><version>3.1.3</version></dependency>…...

linux远程桌面管理工具xrdp
一、概述 我们知道,我们日常通过vnc来远程管理linux图形界面,今天分享一工具Xrdp,它是一个开源工具,允许用户通过Windows RDP访问Linux远程桌面。 除了Windows RDP之外,xrdp工具还接受来自其他RDP客户端的连接…...

100天精通Python(可视化篇)——第106天:Pyecharts绘制多种炫酷桑基图参数说明+代码实战
文章目录 专栏导读一、桑基图介绍1. 桑基图是什么?2. 桑基图应用场景?二、桑基图配置选项1. 导包2. add函数3. 分层设置三、桑基图基础1. 普通桑基图2. 修改标签位置3. 修改节点布局方向4、月度开支桑基图书籍推荐专栏导读 🔥🔥本文已收录于《100天精通Python从入门到就…...

什么是OTP认证?OTP认证服务器有哪些应用场景?
OTP是一次性密码,即只能使用一次的密码。它基于专门的算法,每隔60秒生成一个不可预测的随机数字组合。这种密码的有效期仅在一次会话或交易过程中,因此不容易受到重放攻击。在计算器系统或其他数字设备上,OTP是一种只能使用一次的…...

shell_73.Linux使用新 shell 启动脚本
每次启动新 shell,bash shell 都会运行.bashrc 文件。①对此进行验证,可以使用这种方法:在 主目录下的.bashrc 文件中加入一条简单的 echo 语句,然后启动一个新 shell。 $ cat $HOME/.bashrc # .bashrc # Source global definiti…...

【领域驱动设计】聚合
从战术设计上,DDD 最值得借鉴的就是聚合根 什么是聚合 将实体和值对象在一致性边界之内组合聚合 这里的一致性包括 1、业务概念的完整性 2、业务规则的一致性:多个实体需要在一次操作中保持某种一致性(修改 A,同步必须修改 B&a…...

WiFi模块在智能家居中的应用与优化
智能家居技术的迅速发展已经改变了我们对家庭的定义。WiFi模块作为智能设备连接的核心,扮演着连接和控制智能家居生态系统的关键角色。本文将深入研究WiFi模块在智能家居中的应用,同时探讨如何通过优化来提升其性能和用户体验。 1. 智能家居中WiFi模块的…...

LeetCode75——Day27
文章目录 一、题目二、题解 一、题目 933. Number of Recent Calls You have a RecentCounter class which counts the number of recent requests within a certain time frame. Implement the RecentCounter class: RecentCounter() Initializes the counter with zero r…...

P6462补刀
灵光一现,突然就做出来了 正好写一下思路过程 一开始寻思是个数论的问题,貌似需要用到扩展欧几里得,不管那么多,直接写上,接着不断缝缝补补修修改改,此处省略一小时.... 做不出来....好难受 星期天,无聊,做个题.. 突然,不对啊 这个题实际上不就是我当前打还是不打的一个选…...

Python教程---Python交互界面
当我们通过命令行来输入Python,所进入到的界面就是Python的交互界面 结构: 版本和版权声明: Python 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 16:07:46) [MSC v.1900 32 bit (Intel)] on win32 Type "help", "copyright"…...

基于计算机视觉的身份证识别系统 计算机竞赛
0 前言 🔥 优质竞赛项目系列,今天要分享的是 基于机器视觉的身份证识别系统 该项目较为新颖,适合作为竞赛课题方向,学长非常推荐! 🧿 更多资料, 项目分享: https://gitee.com/dancheng-sen…...

[python] logging输出到控制台(标准输出)
要将logging.info输出到控制台(标准输出),可以使用以下代码: import logging# 创建一个logger对象 logger logging.getLogger(__name__)# 创建一个控制台处理器 console_handler logging.StreamHandler()# 设置控制台处理器的输…...

uniapp 离线打包 google 登录
官方文档: Oauth 模块 | uni小程序SDK 其中有 clientid 和反向url clientid 是 xxxx.apps.googleusercontent.com 反向url 是 com.googleusercontent.apps.xxx...

【实战Flask API项目指南】之一 概述
实战Flask API项目指南之 概述 本系列文章将带你深入探索实战Flask API项目指南,通过跟随小菜的学习之旅,你将逐步掌握Flask在实际项目中的应用。让我们一起踏上这个精彩的学习之旅吧! 前言 小菜是一个Python编程爱好者,他目前…...

AD面试总结
文章目录 CK的面试1.自我介绍2.学习动机3.一天花多久时间4.兴趣爱好5.sql5.1 第二周那道题5.2 对时间盲注和布尔盲注的简单介绍5.3 盲注中可以替代sleep的替代函数 6.反序列化6.1 列举几个函数的触发时机6.2 __wakeup绕过的多种方法6.3 GC垃圾回收机制 7.死亡exit8.mysql8.1.练…...

从今年最硬科幻游戏中的思考
前言 最近有一款“完蛋,我被美女包围了”游戏火爆了,steam上一度达到排行榜第一最低也能到第八(销量据说到了100万份),接下来分享一下自己对于这一款游戏的思考,如果有其他想法,随时可以联系沟…...

Linux多值判断利用case...esac判断
利用这个判断,一定要注意格式的运用,非常容易出错 case $1 in #判断变量的值 "hello") #双引号注意,右括号 echo " afdbab " #语句段,没啥说的 ;; #两个分号结束第一个判断,…...