编辑
阿华代码,不是逆风,就是我疯
你们的点赞收藏是我前进最大的动力!!
希望本文内容能够帮助到你!!
目录
一:CAS指令
1:概念
CAS是CPU中的一条特殊的指令,它的功能就是完成“比较和交换”
2:伪代码例子说明
伪代码:只能表示一种逻辑,并不能实现编译执行
注:CAS指令一般只关注内存当中的值,寄存器当中的值是多少不打紧,用完就不要了
编辑
3:优点
CAS指令不涉及锁,也能保证线程的安全
二:原子类
1:引入
在Java中:
先是操作系统对指令封装成api
然后JVM在对api进行封装,把CAS的api放到了unsafe这个包里(注:这个包里指令会涉及一些系统底层的内容,使用的话是风险操作)
Java标准库中,对CAS再进一步封装,提供了一些工具类,其中最主要的一个工具叫“原子类”
java.util.concurrent.atomic
编辑
2:代码示例
我们还是沿用【JavaEE】——线程的安全问题和解决方式_java线程安全 线程不安全-CSDN博客
这篇文章中,用两个线程去自增到10000计数器这个例子
前面我们是通过synchronized加锁方式解决,这里我们使用原子类进行代码编译
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中持有的值 } }
编辑
这里的++操作,不在是(load,add,save)三条指令了,而是打包成了CAS指令,成为一个天然的原子性指令,这样就避免了,多线程中两者的指令相互穿插执行,也就避免了线程安全问题
(1)方法总结
①.getAndIncrement()——相当于count++
②.incrementAndGet()——相当于++count
3:与volatile的区别
volatile是禁止指令重排序(因为操作非原子性嘛),
4:标准库源码
编辑
编辑
三:CAS是如何避免线程安全问题
核心点:就是通过CAS和while循环的搭配,来确保内存中的值和寄存器当中的值是一样的,
这里的代价就是“自旋”——while循环嘛,但是一般循环不了几次就OK了,这点资源损耗可以忽略
不计,CAS还是很香的~~~
编辑
编辑
编辑
四:CAS中ABA问题
1:引入
上述图文看明白之后,我们可以总结出一点,CAS判断内存和寄存器中的值是否相等,本质上就是在判断——是否有其他线程穿插指令
想象一下,在CAS之前,如果有一个线程穿插进去把数值修改了,紧接着第二个线程也穿插进去把错误的数值又修改回来了,那么CAS是感知不到有线程穿插进来的(如穿~~)
一般来说这也不会引起什么bug之类的
2:极端情况
存钱 编辑
3:解决方案
(1)约定数据变化本身就是单向的,只能增加或者只能减少
(2)对于本身必须就是双向变化的数据,引入一个版本号——这个版本号就是只能增加不能减少