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);
                    // 第二步+1
                    value++;
                    // 第三补+ 回写map
                    concurrentHashMap.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
 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu 20.04(服务器版)安装 Anaconda

0、Anaconda介绍 Anaconda是一个开源的Python发行版本&#xff0c;包含了包括Python、Conda、科学计算库等180多个科学包及其依赖项。因此&#xff0c;安装了Anaconda就不用再单独安装CUDA、Python等。 CUDA&#xff0c;在进行深度学习的时候&#xff0c;需要用到GPU&#xf…

Lnton羚通算法算力云平台在环境配置时 OpenCV 无法显示图像是什么原因?

问题&#xff1a; cv2.imshow 显示图像时报错&#xff0c;无法显示图像 0%| | 0/1 [00:00<…

【java】为什么文件上传要转成Base64?

文章目录 1 前言2 multipart/form-data上传3 Base64上传3.1 Base64编码原理3.2 Base64编码的作用 4 总结 1 前言 最近在开发中遇到文件上传采用Base64的方式上传&#xff0c;记得以前刚开始学http上传文件的时候&#xff0c;都是通过content-type为multipart/form-data方式直接…

易服客工作室:Houzez主题 - 超级房地产WordPress主题/网站

Houzez主题是全球流行的房地产经纪人和公司的WordPress主题。 Houzez Theme是专业设计师创造一流设计的超级灵活起点。它具有您的客户&#xff08;房地产经纪人或公司&#xff09;甚至可能做梦也想不到的功能。 网址&#xff1a;Houzez主题 - 超级房地产WordPress主题/网站 - …

Java请求Http接口-hutool的HttpUtil(超详细-附带工具类)

概述 HttpUtil是应对简单场景下Http请求的工具类封装&#xff0c;此工具封装了HttpRequest对象常用操作&#xff0c;可以保证在一个方法之内完成Http请求。 此模块基于JDK的HttpUrlConnection封装完成&#xff0c;完整支持https、代理和文件上传。 导包 <dependency>&…

[保研/考研机试] KY96 Fibonacci 上海交通大学复试上机题 C++实现

题目链接&#xff1a; KY96 Fibonacci https://www.nowcoder.com/share/jump/437195121692000803047 描述 The Fibonacci Numbers{0,1,1,2,3,5,8,13,21,34,55...} are defined by the recurrence: F00 F11 FnFn-1Fn-2,n>2 Write a program to calculate the Fibon…

基于chatgpt动手实现一个ai_translator

动手实现一个ai翻译 前言 最近在极客时间学习《AI 大模型应用开发实战营》&#xff0c;自己一边跟着学一边开发了一个进阶版本的 OpenAI-Translator&#xff0c;在这里简单记录下开发过程和心得体会&#xff0c;供有兴趣的同学参考&#xff1b; ai翻译程序 版本迭代 在学习…

物联网设备的分类和功能阐述

物联网是将各种物理设备和传感器通过互联网进行连接和交互的网络&#xff0c;物联网的核心思想是让各种设备能够通过互联网实现智能化、自动化和远程控制。物联网设备是指连接到物联网中的各种设备&#xff0c;可以通过传感器、处理器、通信模块等技术实现与互联网的连接和数据…

vue3 简易用对话框实现点击头像放大查看

设置头像悬停手势 img:hover{cursor: pointer;}效果&#xff1a; 编写对话框 <el-dialog class"bigAvatar"style"border-radius: 4px;"v-model"deleteDialogVisible"title"查看头像"top"5px"><div><img src&…

大数据Flink学习圣经:一本书实现大数据Flink自由

学习目标&#xff1a;三栖合一架构师 本文是《大数据Flink学习圣经》 V1版本&#xff0c;是 《尼恩 大数据 面试宝典》姊妹篇。 这里特别说明一下&#xff1a;《尼恩 大数据 面试宝典》5个专题 PDF 自首次发布以来&#xff0c; 已经汇集了 好几百题&#xff0c;大量的大厂面试…

SpringBoot部署到腾讯云

SpringBoot部署到腾讯云 此处默认已经申请到腾讯云服务器&#xff0c;因为本人还没有申请域名&#xff0c;所以就直接使用的ip地址 XShell连接到腾讯云 主机中填写腾讯云的公网ip地址 公网ip地址在下图中找到 接下来填写服务器的用户名与密码 一般centOS用户名为root&#xff…

【设计模式】订单状态流传中的状态机与状态模式

文章目录 1. 前言2.状态模式2.1.订单状态流转案例2.1.1.状态枚举定义2.1.2.状态接口与实现2.1.3.状态机2.1.4.测试 2.2.退款状态的拓展2.2.1.代码拓展2.2.2.测试 2.3.小结 3.总结 1. 前言 状态模式一般是用在对象内部的状态流转场景中&#xff0c;用来实现状态机。 什么是状态…

rabbitmq的消息应答

消费者完成一个任务可能需要一段时间&#xff0c;如果其中一个消费者处理一个长的任务并仅只完成 了部分突然它挂掉了&#xff0c;会发生什么情况。RabbitMQ 一旦向消费者传递了一条消息&#xff0c;便立即将该消 息标记为删除。在这种情况下&#xff0c;突然有个消费者挂掉了…

jvm-类加载子系统

1.内存结构概述 类加载子系统负责从文件系统或网络中加载class文件&#xff0c;class文件在文件开头有特定的文件标识 ClassLoader只负责class文件的加载&#xff0c;至于它是否运行&#xff0c;则由Execution Engine决定 加载的类信息存放于一块称为方法区的内存空间&#xff…

SHELL 基础

echo 打印命令 &#xff1a; 显示字符串 [rootserver ~]# echo this is SHELL language this is SHELL language [rootserver ~]# echo this is SHELL language this is SHELL language [rootserver ~]# echo "this is SHELL language" this is SHELL language…

【java毕业设计】基于ssm+mysql+jsp的个性化影片推荐系统设计与实现(程序源码)-个性化影片推荐系统

基于ssmmysqljsp的个性化影片推荐系统设计与实现&#xff08;程序源码毕业论文&#xff09; 大家好&#xff0c;今天给大家介绍基于ssmmysqljsp的个性化影片推荐系统设计与实现&#xff0c;本论文只截取部分文章重点&#xff0c;文章末尾附有本毕业设计完整源码及论文的获取方式…

Bean 作用域、生命周期和Spring执行流程

文章目录 Bean作用域问题案例分析公共 BeanA 用户使用时修改B 用户使用时原因分析 作用域定义Bean 的6种作用域singletonprototyperequestsessionapplicationwebsocket 设置作用域 Spring 执行流程1、启动容器2、Bean 初始化3、注册Bean对象到容器中4、装配Bean属性 Bean 生命周…

redis 存储结构原理 1

关于 redis 相信大家都不陌生了&#xff0c;之前有从 0 -1 分享过 redis 的基本使用方式&#xff0c;用起来倒是都没有啥问题了&#xff0c;不过还是那句话&#xff0c;会应用之后&#xff0c;我们必须要究其原理&#xff0c;知其然知其所以然 今天我们来分享一下关于 redis 的…

“维度削减+逻辑回归”:如何使用PCA大幅提升乳腺癌的预测成功率?

一、引言 乳腺癌是女性中最常见的恶性肿瘤之一&#xff0c;也影响着全球范围内许多人们的健康。据世界卫生组织&#xff08;WHO&#xff09;的数据&#xff0c;乳腺癌是全球癌症发病率和死亡率最高的肿瘤之一&#xff0c;其对个体和社会的危害不可忽视。因此&#xff0c;早期乳…

Coremail参与编制|《信创安全发展蓝皮书——系统安全分册(2023年)》

信创安全发展蓝皮书 近日&#xff0c;Coremail参与编制的《信创安全发展蓝皮书—系统安全分册&#xff08;2023年&#xff09;》重磅发布。 此次信创安全发展蓝皮书由工业和信息化部电子第五研究所联合大数据协同安全技术国家工程研究中心重磅共同发布。 本次蓝皮书涵盖信创系…