深入理解CAS
什么是CAS?
CAS是原子类的一个方法:compareAndSet CAS是CPU的并发原语,该方法有两个参数,返回类型为布尔值,操作成功返回true,否则返回false
public final boolean compareAndSet(int expect, int update) { //期望,修改 return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
package com.wyh.cas; import java.util.concurrent.atomic.AtomicInteger; /** * @program: JUC * @description: CAS测试 * @author: 魏一鹤 * @createDate: 2022-03-13 20:26 **/ //什么是CAS? 原子类的一个方法compareAndSet 比较并交换 public class CASDemo { public static void main(String[] args){ //创建原子类 初始值为2022 AtomicInteger atomicInteger=new AtomicInteger(2021); //public final boolean compareAndSet(int expect, int update) 期望 更新 //如果期望的值达到了,就更新值,否则就不更新 返回布尔值 System.out.println(atomicInteger.compareAndSet(2021,1999));//true //输出结果 System.out.println(atomicInteger.get()); //1999 //由于当前值已经为1999,无法达到2021 所以改变不成功 System.out.println(atomicInteger.compareAndSet(2021,1999));//false System.out.println(atomicInteger.get()); //1999 } }
true
1999
false
1999
UnSafe类
Java是不能调用内存的,c++可以调用内存,navite和uUnSafe类都是java调用c++调用内存的工具
以下为unsafe中的自增方法 是一个自旋锁
网络异常,图片无法展示
|
CAS简单总结
比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么久执行,如果不是就一直循环
缺点:
1 由于底层是自旋锁,如果期望值达不到的时候回死循环耗时
2 一次性只能保证一个共享变量的原子性
3 存在ABA问题
- CAS的ABA问题(狸猫换太子)
什么是CAS的ABA问题
简单来说,就是两个线程共享一个资源,线程A正常获取,线程B获取之后进行资源改动,但是并未同步告诉给A线程,这时候线程A不知道线程B做了改动,还以为这个资源是原来的资源
类似于乐观锁
package com.wyh.cas; import java.util.concurrent.atomic.AtomicInteger; /** * @program: JUC * @description: CAS测试 * @author: 魏一鹤 * @createDate: 2022-03-13 20:26 **/ //什么是CAS? 原子类的一个方法compareAndSet 比较并交换 public class CASDemo { public static void main(String[] args){ //创建原子类 初始值为2022 AtomicInteger atomicInteger=new AtomicInteger(2021); //===========捣乱的线程============= System.out.println(atomicInteger.compareAndSet(2021,2022));//true System.out.println(atomicInteger.get()); //1999 System.out.println(atomicInteger.compareAndSet(2022,2021)); System.out.println(atomicInteger.get()); //===========期望的线程============= System.out.println(atomicInteger.compareAndSet(2021,6666)); System.out.println(atomicInteger.get()); } }
true
2022
true
2021
true
6666
如何解决这种ABA,就是线程故意捣乱的问题呢,使用原子引入
使用原子引入解决CAS ABA问题
带版本号的原子操作 AtomicStampedReference 第一个参数是期望值,第二个参数是版本号,每次操作的时候先获取版本号,这种思想也是乐观锁的思想
public AtomicStampedReference(V initialRef, int initialStamp) { pair = Pair.of(initialRef, initialStamp); }
package com.wyh.cas; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicStampedReference; /** * @program: JUC * @description: CAS测试 * @author: 魏一鹤 * @createDate: 2022-03-13 20:26 **/ //什么是CAS? 原子类的一个方法compareAndSet 比较并交换 public class CASDemo { public static void main(String[] args){ //创建原子印用 带版本号的原子操作 第一个参数是期望值,第二个参数是版本号 //如果泛型是一个包装类,要注意对象的引入问题,在正常业务种 这里面的泛型是一个对象 AtomicStampedReference<Object> atomicInteger = new AtomicStampedReference<>(1,1); //模拟两个线程去操作 //线程A new Thread(()->{ //每次操作的时候先获取版本号 int stamp = atomicInteger.getStamp(); System.out.println("A1的版本:"+stamp); //休眠2s保证线程执行顺序 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } //CAS设置并替换操作 System.out.println(atomicInteger.compareAndSet(1, 2, atomicInteger.getStamp(), atomicInteger.getStamp() + 1)); System.out.println("A2的版本:"+atomicInteger.getStamp()); System.out.println(atomicInteger.compareAndSet(2, 1, atomicInteger.getStamp(), atomicInteger.getStamp() + 1)); System.out.println("A3的版本:"+atomicInteger.getStamp()); },"A").start(); //线程B new Thread(()->{ //每次操作的时候先获取版本号 int stamp = atomicInteger.getStamp(); System.out.println("B1的版本:"+stamp); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } //CAS操作 System.out.println(atomicInteger.compareAndSet(1, 6, stamp, stamp + 1)); System.out.println("B2的版本:"+atomicInteger.getStamp()); },"B").start(); }