当前位置: 首页 > news >正文

Java多线程~CAS的原理及其应用

目录

什么是CAS

CAS的实现原理

CAS的ABA问题

什么是ABA问题 

解决方案


什么是CAS

CAS(Compare And Swap):比较并交换,CAS是乐观锁的一种实现

一个CAS涉及到的操作

假设内存中的原数据V,旧的预期值A,要修改的的值B

1.比较V与A的值是否相等(Compare)

2.如果比较相等,则将B写入V(Swap)

3.返回操作是否成功

简单的说,CAS就是直接尝试修改变量,再返回修改的结果.比较读和写两个时间点主存中变量的值是否相等,如果相等就把要修改的值写回主存中,如果不相等就不做任何操作,修改成功返回true,修改失败就返回false 

CAS的实现原理

Java中的CAS是基于Unsafe这个类提供的CAS操作 ,Unsafe中的CAS又是基于操作系统和CPU提供的机制来实现的,即硬件予以支持,软件层面才能做到

CAS的ABA问题

什么是ABA问题 

假设有两个线程,t1和t2,有一个共享变量num,初始值为A,t1线程想要使用CAS将A修改为Z,此时t1需要进行如下操作:先从主存中读取num的值并记录下来,之后使用CAS判定当前num的值是否为A,如果为A就修改为Z。但是在这个过程中可能t2线程先将num的值修改为B,又将num的值从B修改回A,这就是CAS的ABA问题

代码示例(模拟CAS的ABA问题)

利用sleep方法来简单模拟ABA问题:

1、t1线程想要修改num的为5,首先t1线程从主存中读取num的值并记录为旧的预期值,此时旧的预期值为2

2、在t1线程开始修改操作之前,t2线程先将主存中的num改为15,再将num从15改回2,此时线程2执行结束退出

3、t1线程继续执行修改操作,从主存中读取到当前num值为2,与预期值相等,就进行修改操作. 

package threadhomework;

/**
 * Created with IntelliJ IDEA.
 * Description: ABA问题示例
 * User: Li_yizYa
 * Date: 2022—09—24
 * Time: 13:23
 */
public class CASDemo {
    private static int num = 2;
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    int oldNum = num;//旧的预期值
                    //利用sleep方法模拟t2在t1获取到共享变量后完成两次修改操作
                    Thread.sleep(200);
                    //如果旧的预期值与当前num的值相等则修改
                    if(oldNum == num) {
                        num = 5;
                        System.out.println("t1线程修改成功,修改后num的值为: " + num);
                    } else {
                        System.out.println("t1线程修改失败");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                num = 15;
                System.out.println("t2线程第一次修改num,修改为: " + num);
                num = 2;
                System.out.println("t2线程第二次修改num,修改为: " + num);
            }
        });
        t1.start();
        //保证t1线程先执行
        Thread.sleep(100);
        t2.start();
    }
}

 

解决方案

给要修改的值引入版本号,在CAS比较数据当前值和旧值的同时,也要比较版本号是否符合预期.

如果旧值与当前值相同,则开始比较版本号:

· 如果当前版本号与旧的预期版本号相同,则进行修改,版本号+1

· 如果当前版本号与旧的预期版本号不相同,则修改失败.

在Java标准库中提供了AtomicStampedReference<E>类,这个类可以对某个类进行包装,在内部就提供了版本管理的功能.

例如上述ABA问题演示代码,再加上一个版本号就可以解决ABA问题

package threadhomework;

/**
 * Created with IntelliJ IDEA.
 * Description: ABA问题示例
 * User: Li_yizYa
 * Date: 2022—09—24
 * Time: 13:23
 */
public class CASDemo {
    private static int num = 2;
    private static int version = 1;
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    int oldNum = num;
                    int oldVersion = version;
                    Thread.sleep(200);
                    if(oldNum == num && oldVersion == version) {
                        num = 5;
                        //修改成功,版本号+1
                        version++;
                        System.out.println("t1线程修改成功,修改后num的值为: " + num + " 当前版本号为: " + version);
                    } else {
                        System.out.println("t1线程修改失败,版本号不匹配 旧的预期版本号为: " + oldVersion + " 当前版本号为: " + version);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                num = 15;
                version++;//修改成功,版本号+1
                System.out.println("t2线程第一次修改num,修改为: " + num + " 当前版本号为: " + version);
                num = 2;
                version++;//修改成功,版本号+1
                System.out.println("t2线程第二次修改num,修改为: " + num + " 当前版本号为: " + version);
            }
        });
        t1.start();
        Thread.sleep(100);
        t2.start();
    }
}

 

 

 

 

相关文章:

  • [CSS]盒子模型
  • 【 C++ 】开散列哈希桶的模拟实现
  • 阿里云数据库(RDS)查看空间使用情况
  • 【C++编程语言】之 文件操作
  • 人生模式 - 如何跟潜意识对话
  • ubuntu18.04安装redis
  • 02 LaTeX文字实战应用
  • Flash:Flash动画设计软件界面的简介、Flash AS 3.0代码编程入门教程之详细攻略
  • C语言进阶——自定义类型
  • 微信公众号网课查题系统
  • golang学习笔记系列之函数
  • VJ_Dressing_思维
  • 关于我的vsc不能远程debug这件事
  • [English]英语积累本
  • java-php-python-ssm爱馨敬老院网站计算机毕业设计
  • [译]Python中的类属性与实例属性的区别
  • Bootstrap JS插件Alert源码分析
  • CSS居中完全指南——构建CSS居中决策树
  • CSS魔法堂:Absolute Positioning就这个样
  • extract-text-webpack-plugin用法
  • GitUp, 你不可错过的秀外慧中的git工具
  • JavaScript 基础知识 - 入门篇(一)
  • JavaScript中的对象个人分享
  • js中的正则表达式入门
  • mysql_config not found
  • Solarized Scheme
  • vue 个人积累(使用工具,组件)
  • Vue官网教程学习过程中值得记录的一些事情
  • 搭建gitbook 和 访问权限认证
  • 工作手记之html2canvas使用概述
  • 函数式编程与面向对象编程[4]:Scala的类型关联Type Alias
  • 回顾2016
  • 区块链分支循环
  • 使用阿里云发布分布式网站,开发时候应该注意什么?
  • 小程序、APP Store 需要的 SSL 证书是个什么东西?
  • 怎么将电脑中的声音录制成WAV格式
  • gunicorn工作原理
  • ​油烟净化器电源安全,保障健康餐饮生活
  • # 达梦数据库知识点
  • #13 yum、编译安装与sed命令的使用
  • (1)Android开发优化---------UI优化
  • (2)MFC+openGL单文档框架glFrame
  • (delphi11最新学习资料) Object Pascal 学习笔记---第5章第5节(delphi中的指针)
  • (Matlab)基于蝙蝠算法实现电力系统经济调度
  • (动态规划)5. 最长回文子串 java解决
  • (二)丶RabbitMQ的六大核心
  • (二十三)Flask之高频面试点
  • (附源码)计算机毕业设计SSM疫情社区管理系统
  • (个人笔记质量不佳)SQL 左连接、右连接、内连接的区别
  • (简单有案例)前端实现主题切换、动态换肤的两种简单方式
  • (七)Java对象在Hibernate持久化层的状态
  • (三)centos7案例实战—vmware虚拟机硬盘挂载与卸载
  • (四)库存超卖案例实战——优化redis分布式锁
  • (一)appium-desktop定位元素原理
  • (正则)提取页面里的img标签