Java EE 多线程之 CAS

文章目录

  • 1. 什么是 CAS
  • 2. CAS 有那些应用
    • 2.1 实现原子类
    • 2.2 实现自旋锁
    • 2.3 ABA 问题
      • 2.3.1 ABA 问题的解决方案

1. 什么是 CAS

CAS:全称Compare and swap


假如有一个内存 M
有两个寄存器 A B
CAS(M, A, B)
如果 M 和 A 的值相同的话,就把 M 和 B 里的值进行交换,同时整个操作返回 true
如果 M 和 A 的值不同的话,无事发生,同时整个操作返回 false

这里交换的本质,是为了把 B 赋值给 M
寄存器 B 的值不太关心,主要的是 M 里面的情况


CAS 伪代码

boolean CAS(address, expectValue, swapValue) {if (&address == expectedValue) {&address = swapValue;return true;}return false;}

上⾯写的代码不是原⼦的,真实的 CAS 是⼀个原⼦的硬件指令完成的,这个伪代码只是辅助理解 CAS 的⼯作流程

CAS 其实是一个 cpu 指令
单个的 cpu 指令,是原子的,就可使用 CAS 完成一些操作,进一步替代“加锁”

基于 CAS 实现线程安全的方式,也称为“无锁编程”
优点
保证线程安全,同时避免阻塞(提高效率)
缺点
1.代码会更复杂,不好理解
2.只能够适合一些特定场景,不如加锁方式更普适

CAS 本质上是 cpu 提供的指令;后来又被操作系统封装,提供成 api;然后又被 JVM 封装,也提供成 api;最后被程序员使用

2. CAS 有那些应用

2.1 实现原子类

在前面进行多线程编写的时候,我们说过int++
int,进行++,不是原子的(load,add,save)

但是标准库中提供了一个类,就是原子的
AtomicInteger,就是基于CAS 的方式对 int 进行封装,此时进行 ++,就是原子的了

import java.util.concurrent.atomic.AtomicInteger;public class ThreadDemo34 {// 不使用原生的 int, 而是替换成 AtomicInteger// private static int count = 0;private static AtomicInteger count = new AtomicInteger(0);public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < 50000; i++) {// count++;count.getAndIncrement();// ++count;//count.incrementAndGet();// count += n;//count.getAndAdd(n);// count--;//count.getAndDecrement();// --count;//count.decrementAndGet();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 50000; i++) {count.getAndIncrement();}});t1.start();t2.start();t1.join();t2.join();System.out.println("count = " + count.get());}}

在这里插入图片描述
这个时候就是线程安全的了


我们可以查看一下原码
在这里插入图片描述
在java 中,有些操作是偏底层的操作,偏底层的操作早使用的时候就会有更多的注意事项
稍有不慎就容易写出问头
这些操作,就放到了 unsafe 中进行归类
在这里插入图片描述
在原子类的内部没有使用 synchronized 加锁
在这里插入图片描述
再内部就无法看到了
native 修饰的方法,称为“本地方法”
就是在 JVM 源码中,使用 c++ 实现的逻辑
这就涉及到一些底层操作

结论:原子类里面是基于 CAS 来实现的
在这里插入图片描述

2.2 实现自旋锁

自旋锁伪代码

public class SpinLock {private Thread owner = null;public void lock(){// 通过 CAS 看当前锁是否被某个线程持有.// 如果这个锁已经被别的线程持有, 那么就⾃旋等待.// 如果这个锁没有被别的线程持有, 那么就把 owner 设为当前尝试加锁的线程.while(!CAS(this.owner, null, Thread.currentThread())){}}public void unlock (){this.owner = null;}
}

private Thread owner 记录当前这个锁被那个线程获取到了,如果是 null,表示未加锁状态

2.3 ABA 问题

CAS 进行操作的关键,是通过 值“没有发生变化” 来作为 “没有其他线程穿插执行” 的判定依据

但是这种判定方式,不够严谨
在更极端的情况下,可能有另一个线程穿插进来,把值从 A 改成 B,再从 B 改成 A
针对以一个线程来说,看起来好像是这个值没有改变,但是实际上已经穿插执行了
这就是 ABA 问题

ABA 问题如果真的出现了,其实大部分情况下也不会产生 bug ,虽然另一个线程穿插执行了,由于值又改回去了,此时逻辑上也不一定会产生 bug

ABA 问题通常不会有 bug,但是极端情况下可能会出现问题
假设一个场景,我去 ATM 取钱,我本身的账户有 1000
我需要去 500,但是发生了 bug
安全取钱按钮,没有发生反应,又按了一下此时就产生了两个线程进行扣款操作
在这里插入图片描述

2.3.1 ABA 问题的解决方案

只要让判定的数值,按照一个方向增长即可(不要反复)
如果有增有减,就可能出现 ABA
只是增加,或者只是减少,就不会出现ABA

但是像账户余额这样的概念,本身就应该能增能减
可以引入一个额外的变量,版本号
约定每次修余额,都要让版本号自增

此时在使用 CAS 判定的时候,就不是直接判定余额了,而是判定版本号,看版本号是否是变化的了
如果版本号不变,注定没有线程穿插执行了

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

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

相关文章

和鲸科技携手深圳数据交易所,“数据+数据开发者生态”赋能人工智能产业发展

信息化时代&#xff0c;数据驱动决策的重要性日益凸显。通过利用数据可以深入了解市场需求、客户行为、竞争态势等关键信息&#xff0c;从而制定更为有效的战略和决策。围绕推动数据要素产业发展&#xff0c;近日&#xff0c;深圳数据交易所&#xff08;以下简称“深数所”&…

网络安全——Iptables防DDoS攻击实验

一、实验目的要求&#xff1a; 二、实验设备与环境&#xff1a; 三、实验原理&#xff1a; 四、实验步骤&#xff1a; 五、实验现象、结果记录及整理&#xff1a; 六、分析讨论与思考题解答&#xff1a; 一、实验目的要求&#xff1a; 1、掌握常见DDoS攻击SYN Flood的攻击…

PyTorch张量:内存布局

你可能对 torch 上的某些函数感到困惑&#xff0c;它们执行相同的操作但名称不同。 例如&#xff1a; reshape()、view()、permute()、transpose() 等。 这些函数的做法真的不同吗&#xff1f; 不&#xff01; 但为了理解它&#xff0c;我们首先需要了解一下张量在 pytorch 中…

scala集合_数组_元组_映射_列表

数组元组映射列表 1.11 集合&#xff08;scala.collection&#xff09; 集合是一种用来存储各种对象和数据的容器。Scala 集合分为可变的和不可变的集合。 1. 不可变集合可以安全的并发访问。 2. 可变集合可以在适当的地方被更新或扩展。这意味着你可以修改&#xff0c;添加…

二叉树--基础OJ

1.对称二叉树 题目链接&#xff1a;101. 对称二叉树 - 力扣&#xff08;LeetCode&#xff09; 题解&#xff1a; 我们可以用递归的方法去做&#xff1a; 如果两个树互为镜像&#xff08;1.根节点的值相同&#xff0c;2.左子树的值与右子树的值对称&#xff09;则为对称二叉树&a…

专栏十五:omicverse在单细胞分析中的实际使用体验和小改动

写一些自己的理解吧,一些小步骤,正在更新中。。。 安装 原作者写的很清楚了 大部分直接抄 个别地方:去选择下载适合自己的pytorch版本PyTorch, 比如我的是cuda12,下载命令实际是 pip3 install torch torchvision torchaudio 查看cuda版本命令 nvidia-smi 当然还有个命…

04-Nacos中负载均衡规则的配置

负载均衡规则 同集群优先 默认的ZoneAvoidanceRule实现并不能根据同集群优先的规则来实现负载均衡,Nacos中提供了一个实现叫NacosRule可以优先从同集群中挑选服务实例 当服务消费者在本地集群找不到服务提供者时也会去其他集群中寻找,但此时会在服务消费者的控制台报警告 第…

BMP转PNG工具BMP2PNG

自己写的一个BMP转PNG工具BMP2PNG 写这个工具是因为要使用传奇的部分素材在COCOS2DX使用&#xff0c; 但是COCOS2DX不支持BMP 如果直接将BMP转换到PNG的话&#xff0c;网上找到的工具都不支持透明色转换。难道要用PS一个一个抠图吗&#xff1f;要累死 所以写了这个工具。一些古…

《昆虫记》带我进入了一个昆虫的新世界。

《昆虫记》带我进入了一个昆虫的新世界。 《昆虫记》的作者是法国作家亨利.法布尔&#xff0c;他是法国著名的文学家&#xff0c;昆虫学家&#xff0c;科普作家。被世人称为“科学界的诗人”&#xff0c;“昆虫世界的荷马”&#xff0c;“昆虫世界的维吉尔”&#xff0c;他从小…

I2C 应用编程

1. I2C 框架结构 1.1 I2C 硬件框架 I2C 总线拓扑图 ⚫ 在一个芯片 (SoC) 内部&#xff0c;有一个或多个 I2C 控制器 ⚫ 在一个 I2C 控制器上&#xff0c;可以连接一个或多个 I2C 设备 ⚫ I2C 总线只需要 2 条线&#xff1a;时钟线 SCL 、数据线 SDA ⚫ 在 …

SpringCloud面试题——Sentinel

一&#xff1a;什么是Sentinel&#xff1f; Sentinel是一个面向分布式架构的轻量级服务保护框架&#xff0c;实现服务降级、服务熔断、服务限流等功能 二&#xff1a;什么是服务降级&#xff1f; 比如当某个服务繁忙,不能让客户端的请求一直等待,应该立刻返回给客户端一个备…

C# 图解教程 第5版 —— 第18章 泛型

文章目录 18.1 什么是泛型18.2 C# 中的泛型18.3 泛型类18.3.1 声明泛型类18.3.2 创建构造类型18.3.3 创建变量和实例18.3.4 使用泛型的示例18.3.5 比较泛型和非泛型栈 18.4 类型参数的约束18.4.1 Where 子句18.4.2 约束类型和次序 18.5 泛型方法18.5.1 声明泛型方法18.5.2 调用…