CAS 一些隐藏的知识,您了解吗

目录

ConcurrentHashMap 一定是线程安全的吗

CAS 机制的注意事项

使用java 并行流 ,您要留意了


      ConcurrentHashMap 在JDK1.8中ConcurrentHashMap 内部使用的是数组加链表加红黑树的结构,通过CAS+volatile或synchronized的方式来保证线程安全的,这些原理已毋庸置疑,一言不合上代码.

     1.  模拟2个线程累计,通过ConcurrentHashMap 储存累计的结果。


/*** @description: ConcurrentHashMap 真的安全吗* @author: ppx* @date: 2023/8/17 14:11* @version: 1.0*/
public class TestMap {private static ConcurrentHashMap<String, Integer> concurrentHashMap = new ConcurrentHashMap<>();private static String key = "hello";/*** @description: 测试2个线程 执行计算* @param:* @return: void* @author: ppx* @date: 2023/8/17 16:43*/private static void testRun() {ExecutorService executor = new ThreadPoolExecutor(2, 5,2L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());for (int i = 0; i < 2; i++) {executor.submit(() -> {for (int j = 0; j < 5; j++) {// 第一步读取int value = concurrentHashMap.getOrDefault(key, 0);// 第二步+1value++;// 第三补+ 回写mapconcurrentHashMap.put(key, value);}});}executor.shutdown();// 直到线程执行完成while(!executor.isTerminated()){}System.out.println("执行结果:" + concurrentHashMap.get(key));}public static void main(String[] args) {testRun();}
}

2.出乎意料执行多次输出不同的结果

 

 

3. 分析原理:ConcurrentHashMap 本身是线程安全的,但for 里面的获map取值、加加操作及回写map 这三步是非原子性。要保证操作的安全性,这三步实现原子性即可。

 优化后代码

 private static void testRun() {ExecutorService executor = new ThreadPoolExecutor(2, 5,2L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());for (int i = 0; i < 2; i++) {executor.submit(() -> {for (int j = 0; j < 5; j++) {synchronized (TestMap.class) {int value = concurrentHashMap.getOrDefault(key, 0);value ++;concurrentHashMap.put(key, value);}}});}executor.shutdown();while (!executor.isTerminated()) {}System.out.println("执行结果:" + concurrentHashMap.get(key));}

CAS 机制的注意事项

       某线程把数据A更新了B,随后又从B更新成A,恰好此时另一线程读取该数据,发现数据的值还是A没有变化,误认为还是原来的A,但此时A的一些属性或状态已经发生过变化。

      CAS操作中将判断“V的值是否仍然为A?”,如果是的话将执行更新操作,在某些CAS操作中,如果V的值首先由A变为B,再由B变为A,那么CAS仍然将会操作成功。

ABA问题

          线程A 的操作,cas中的值由1变成99,再由99变成1,此次线程B 发现AtomicInteger 的值还是1,于是更新到50,产生ABA的问题。

private static AtomicInteger atomicInteger = new AtomicInteger(1);public static void main(String[] args) {Thread threadA  =  new Thread(() -> {atomicInteger.compareAndSet(1, 99);atomicInteger.compareAndSet(99, 1);System.out.println("线程A进行CAS后的值:"+atomicInteger.get());try {Thread.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}}, "线程A");Thread threadB  =  new Thread(() -> {try{atomicInteger.compareAndSet(1, 50);System.out.println("线程B进行CAS后的值:"+atomicInteger.get());}catch (Exception e) {e.printStackTrace();}}, "线程B");threadA.start();try {threadA.join();} catch (InterruptedException e) {e.printStackTrace();}threadB.start();}

基于AtomicStampedReference类实现
AtomicStampedReference内部增加了版本号的概念,只有期待的值与版本号分别匹配后,才满足条件,更新最新的值。

案例

      线程 A  进行CAS 操作更新时,发布版本已发生变动,CAS更新 失败。线程B   进行CAS 操作更新时,匹配对应的版本,期待值,更新成功。

 public static void main(String[] args) {new Thread(() -> {// 让线程B 获取最新版本号,成功 执行更新try {Thread.sleep(11);} catch (InterruptedException e) {e.printStackTrace();}int stamp = atomicStampedReference.getStamp();System.out.println(Thread.currentThread().getName() + ", 当前版本号为:" + stamp);boolean firstCasFlag = atomicStampedReference.compareAndSet(100, 99, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);System.out.println("当前版本号:"+atomicStampedReference.getStamp()+", 线程A进行CAS后的值:" + atomicInteger.get() + ",第1次操作是否修改成功: " + firstCasFlag);}, "线程A").start();new Thread(() -> {try {int stamp = atomicStampedReference.getStamp();System.out.println(Thread.currentThread().getName() + ", 版本号为:" + atomicStampedReference.getStamp());boolean flag = atomicStampedReference.compareAndSet(100, 888, stamp, atomicStampedReference.getStamp() + 1);System.out.println("线程B进行CAS后的值:" + atomicStampedReference.getReference() + ", 此次操作是否修改成功: " + flag);} catch (Exception e) {e.printStackTrace();}}, "线程B").start();}

执行结果

线程B, 版本号为:1
线程B进行CAS后的值:888, 此次操作是否修改成功: true
线程A, 当前版本号为:2
当前版本号:2, 线程A进行CAS后的值:1,第1次操作是否修改成功: false
 

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

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

相关文章

TikTok或将于8月底关闭半闭环、速卖通或将推出“半托管”模式

《出海周报》是运营坛为外贸企业主和外贸人独家打造的重要资讯栏目&#xff0c;聚焦企业出海、海外市场动态、海外监管政策等方面&#xff0c;以简捷的方式&#xff0c;提升读者获取资讯的效率。 接下来运营坛为大家带来第15期出海周报&#xff0c;快来看看这周国内外市场发生了…

C++--红黑树

1.什么是红黑树 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制&#xff0c;红黑树确保没有一条路径会比其他路径长出俩倍&#xff0c;因…

【Unity细节】Unity中的层级LayerMask

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 &#x1f636;‍&#x1f32b;️收录于专栏&#xff1a;unity细节和bug &#x1f636;‍&#x1f32b;️优质专栏 ⭐【…

Mybatis Plus中使用LambdaQueryWrapper进行分页以及模糊查询对比传统XML方式进行分页

传统的XML分页以及模糊查询操作 传统的XML方式只能使用limit以及offset进行分页&#xff0c;通过判断name和bindState是否为空&#xff0c;不为空则拼接条件。 List<SanitationCompanyStaff> getSanitationStaffInfo(Param("name") String name,Param("bi…

前端---需要了解浏览器相关知识--浏览器请求服务器资源---缓存

知识点1: 掘金1&#xff1a;浏览器缓存 掘金2 :浏览器缓存 一、浏览器缓存 请求&#xff08;静态资源 &#xff5c; 动态资源&#xff09; 一、缓存是什么&#xff1f; 如果没有缓存的机制 每次都要重新请求静态资源 1.从网络上的下载时间&#xff0c;肯定大于从硬盘里读的…

【S32K 进阶之旅】S32K 芯片的解锁

在使用 S32K1xx MCU 的过程中&#xff0c;因为某些不当操作导致芯片被锁、加密的情况偶有发生&#xff0c;在此总结一篇如何解锁芯片的文档&#xff0c;希望能够帮到有需要的人。 1. S32K 芯片被锁的现象及原因分析1&#xff09;在S32K 系列 MCU 开发和生产过程中&#xff…

常用系统命令

重定向 cat aa.txt > bbb.txt 将输出定向到bbb.txt cat aaa.txt >> bbb.txt 输出并追加查看进程 ps ps -ef 显示所有进程 例⼦&#xff1a;ps -ef | grep mysql |&#xff1a;管道符 kill pid 结束进程&#xff0c; 如 kill 3732&#xff1b;根据进程名结束进程可以先…

Qt使用qml(QtLocation)显示地图

一、qt版本和QtLocation模块版本确认 如果qt版本过低的话是没有QtLocation模块的&#xff0c;我的版本如下 构建工具版本如下 二、qml代码编写 1、工程中添加模块 首先在工程中添加模块quickwidgets positioning location 2、添加资源文件 3、在资源文件中添加qml文件 …

Jenkins改造—nginx配置鉴权

先kill掉8082的端口进程 netstat -natp | grep 8082 kill 10256 1、下载nginx nginx安装 EPEL 仓库中有 Nginx 的安装包。如果你还没有安装过 EPEL&#xff0c;可以通过运行下面的命令来完成安装 sudo yum install epel-release 输入以下命令来安装 Nginx sudo yum inst…

windows上的docker自动化部署到服务器脚本

1、mvn install后&#xff0c;双击这个bat&#xff0c;实现docker build后上传到124服务器&#xff0c;并且重启124服务器 **echo offsetlocal:: 定义镜像名称和版本变量 set IMAGE_NAMEweb set IMAGE_VERSION1.3.1:: 清理本地文件 echo Cleaning up... del service-%IMAGE_N…

中大许少辉博士中国建筑出版传媒八一新书《乡村振兴战略下传统村落文化旅游设计》百度百科新闻

中大许少辉博士中国建筑出版传媒八一新书《乡村振兴战略下传统村落文化旅游设计》百度百科新闻&#xff1a; 乡村振兴战略下传统村落文化旅游设计 - 百度百科 https://baike.baidu.com/item/乡村振兴战略下传统村落文化旅游设计/62588677 概览 《乡村振兴战略下传统村落文化旅游…

c++——静态成员变量、静态成员函数和const修饰的静态成员变量

一、c静态成员变量 在C中&#xff0c;静态成员变量&#xff08;Static Member Variable&#xff09;是类的一种特殊类型的成员变量&#xff0c;它被类的所有实例共享&#xff0c;而不是每个实例都有自己的副本。静态成员变量在类的所有对象之间保持唯一的状态&#xff0c;具有…

Redisson实现分布式锁示例

一、引入依赖 <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.16.0</version></dependency>二、配置类 import org.redisson.Redisson; import org.redisson.api.RedissonClient;…

Hadoop学习:深入解析MapReduce的大数据魔力(三)

Hadoop学习&#xff1a;深入解析MapReduce的大数据魔力&#xff08;三&#xff09; 3.5 MapReduce 内核源码解析3.5.1 MapTask 工作机制3.5.2 ReduceTask 工作机制3.5.3 ReduceTask 并行度决定机制 3.6 数据清洗&#xff08;ETL&#xff09;1&#xff09;需求2&#xff09;需求…

Java数据库连接池原理及spring boot使用数据库连接池(HikariCP、Druid)

和线程池类似&#xff0c;数据库连接池的作用是建立一些和数据库的连接供需要连接数据库的业务使用&#xff0c;避免了每次和数据库建立、销毁连接的性能消耗&#xff0c;通过设置连接池参数可以防止建立连接过多导致服务宕机等&#xff0c;以下介绍Java中主要使用的几种数据库…

【学习FreeRTOS】第11章——FreeRTOS中任务相关的其他API函数

1.函数总览 序号函数描述1uxTaskPriorityGet()获取任务优先级2vTaskPrioritySet()设置任务优先级3uxTaskGetNumberOfTasks()获取系统中任务的数量4uxTaskGetSystemState()获取所有任务的状态信息5vTaskGetInfo()获取单个任务的状态信息6xTaskGetCurrentTaskHandle()获取当前任…

Excel自动化办公——Openpyxl的基本使用

Excel自动化办公——Openpyxl的基本使用 个人感觉&#xff0c;相比Pandas&#xff0c;openpyxl对Excel的操作更为细致&#xff0c;Pandas则更适用于统计计算&#xff1b; 01 基本环境02 Excel数据读取操作03 案例04 向Excel写入数据05 表数据定向修改06 单元格样式制定07 单元…

原型对象的简单了解

在前面学习java有一个概念叫做继承&#xff0c;方便我们对父类方法、变量等的调用。对前端的学习我们需要让对象可以访问和继承其他对象的属性和方法&#xff0c;就需要了解原型对象&#xff0c;以及原型链。 一、原型 构造函数通过原型分配的函数是所有对象所共享的。每一个构…

回归预测 | MATLAB实现SOM-BP自组织映射结合BP神经网络多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现SOM-BP自组织映射结合BP神经网络多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现SOM-BP自组织映射结合BP神经网络多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09;效果一览基本介绍…

【css动画】向下的动态箭头

前言 使用css实现一组向下的动态箭头效果&#xff0c;如下图 思路 1.使用svg画箭头 2.设置keyframes&#xff0c;主要是每个箭头加不同的延时。 代码 <div class"down-arrow"><svg id"more-arrows"><polygonclass"arrow-top&quo…