【JavaEE】——CAS指令和ABA问题

news/2024/10/7 18:49:38/

8e19eee2be5648b78d93fbff2488137b.png

阿华代码,不是逆风,就是我疯

你们的点赞收藏是我前进最大的动力!!

希望本文内容能够帮助到你!!

目录

一:CAS指令

1:概念

2:伪代码例子说明

3:优点

二:原子类

1:引入

2:代码示例

3:与volatile的区别

4:标准库源码

三:CAS是如何避免线程安全问题

四:CAS中ABA问题

1:引入

2:极端情况

3:解决方案


一:CAS指令

1:概念

CAS是CPU中的一条特殊的指令,它的功能就是完成“比较和交换”

2:伪代码例子说明

伪代码:只能表示一种逻辑,并不能实现编译执行

注:CAS指令一般只关注内存当中的值,寄存器当中的值是多少不打紧,用完就不要了

1c8c72bca32d460da5b549d1d5b66cc7.png

3:优点

CAS指令不涉及锁,也能保证线程的安全

二:原子类

1:引入

在Java中:

先是操作系统对指令封装成api

然后JVM在对api进行封装,把CAS的api放到了unsafe这个包里(注:这个包里指令会涉及一些系统底层的内容,使用的话是风险操作)

Java标准库中,对CAS再进一步封装,提供了一些工具类,其中最主要的一个工具叫“原子类”

java.util.concurrent.atomic

46690935fa5740c0a57ea8c8e57d7b08.png

2:代码示例

我们还是沿用【JavaEE】——线程的安全问题和解决方式_java线程安全 线程不安全-CSDN博客

这篇文章中,用两个线程去自增到10000计数器这个例子

前面我们是通过synchronized加锁方式解决,这里我们使用原子类进行代码编译

java">package thread;import java.util.concurrent.atomic.AtomicInteger;/*** Created with IntelliJ IDEA.* Description:* User: Hua YY* Date: 2024-09-29* Time: 14:15*/
public class ThreadDemon36 {// private static int result = 0;//括号里的参数就是result的初始值private static AtomicInteger result = new AtomicInteger(0);public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for(int i = 1 ; i <= 5000 ; i++){result.getAndIncrement();//自增相当于result++//result++;}});Thread t2 = new Thread(() -> {for(int i = 1 ; i <= 5000 ; i++){result.getAndIncrement();//自增//result++;}});t1.start();t2.start();t1.join();t2.join();System.out.println(result.get());//获取到result中持有的值}
}

 d95117553dff4069b86164e440df43fc.png

这里的++操作,不在是(load,add,save)三条指令了,而是打包成了CAS指令,成为一个天然的原子性指令,这样就避免了,多线程中两者的指令相互穿插执行,也就避免了线程安全问题

(1)方法总结

①.getAndIncrement()——相当于count++

②.incrementAndGet()——相当于++count

3:与volatile的区别

volatile是禁止指令重排序(因为操作非原子性嘛),

4:标准库源码

f24e028c87a44c8783646915f83b4cbb.png

17844d4d3272428cb32eb6207dfee80d.png

三:CAS是如何避免线程安全问题

核心点:就是通过CAS和while循环的搭配,来确保内存中的值和寄存器当中的值是一样的, 

这里的代价就是“自旋”——while循环嘛,但是一般循环不了几次就OK了,这点资源损耗可以忽略

不计,CAS还是很香的~~~

b394bf9f41d14493abcb5009969fb1a6.png

b2918816e1e04b348c32129bfc243214.png

 

四:CAS中ABA问题

1:引入

上述图文看明白之后,我们可以总结出一点,CAS判断内存和寄存器中的值是否相等,本质上就是在判断——是否有其他线程穿插指令

想象一下,在CAS之前,如果有一个线程穿插进去把数值修改了,紧接着第二个线程也穿插进去把错误的数值又修改回来了,那么CAS是感知不到有线程穿插进来的(如穿~~)

一般来说这也不会引起什么bug之类的

2:极端情况

存钱fadb290b30264d36b31b76e4aef8c259.png

3:解决方案

(1)约定数据变化本身就是单向的,只能增加或者只能减少

(2)对于本身必须就是双向变化的数据,引入一个版本号——这个版本号就是只能增加不能减少


http://www.ppmy.cn/news/1535822.html

相关文章

如何修改Nuget包的缓存路径

默认Nuget包的缓存路径是%userprofile%\.nuget\packages&#xff0c;例如C:\Users\Administrator\.nuget\packages。 因为这个路径是在C盘下的&#xff0c;所以在使用久了以后&#xff0c;会导致C盘容量骤减。 正常在Visual Studio中引入Nuget包时&#xff0c;会先在缓存文件夹…

WooCommerce与wordpress是什么关系

WooCommerce与WordPress之间的关系非常紧密&#xff0c;因为WooCommerce实际上是一个为WordPress设计的插件。WordPress是一个内容管理系统(CMS)&#xff0c;广泛用于创建各种类型的网站&#xff0c;包括博客、企业网站等。而WooCommerce则是一个免费且开源的电子商务插件&…

计算机毕业设计 基于Python的社交音乐分享平台的设计与实现 Python+Django+Vue 前后端分离 附源码 讲解 文档

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

RTSP作为客户端 推流 拉流的过程分析

之前写过一个 rtsp server 作为服务端的简单demo 这次分析下 rtsp作为客户端 推流和拉流时候的过 A.作为客户端拉流 TCP方式 1.Client发送OPTIONS方法 Server回应告诉支持的方法 2.Client发送DESCRIPE方法 这里是从海康摄像机拉流并且设置了用户名密码 Server回复未认证 3.客…

useEffect 与 useLayoutEffect 的区别

useEffect 与 useLayoutEffect 的区别 useEffect和useLayoutEffect是处理副作用的React钩子函数&#xff0c;有以下区别1. 执行时机不同2. 对性能影响不同3. 对渲染的影响不同&#xff1a;4. 使用场景不同 使用建议 useEffect和useLayoutEffect是处理副作用的React钩子函数&…

华硕天选笔记本外接音箱没有声音

系列文章目录 文章目录 系列文章目录一.前言二.解决方法第一种方法第二种方法 一.前言 华硕天选笔记本外接音箱没有声音&#xff0c;在插上外接音箱时&#xff0c;系统会自动弹出下图窗口 二.解决方法 第一种方法 在我的电脑上选择 Headphone Speaker Out Headset 这三个选项…

【java】数据类型与变量以及操作符

各位看官&#xff1a;如果您觉得这篇文章对您有帮助的话 欢迎您分享给更多人哦 感谢大家的点赞收藏评论&#xff0c;感谢您的支持&#xff01;&#xff01;&#xff01; 目录 一.字面变量&#xff1a; 二&#xff1a;数据类型 1.1&#xff1a;int类型&#xff1a;&#xff0…

谷粒商城のRabbitMQ基础篇

文章目录 前言一、Rabbit MQ简介1、基本概念2、组件架构 二、使用步骤1.引入依赖2.application.properties3、docker 安装Rabbit MQ3、使用案例3.1、定义队列3.2、定义交换机3.3、绑定3.4、发送消息3.5、接受消息3.5、自定义消息序列化方式3.6、演示Fanout 交换机模式3.7、演示…