Java--JUC--CAS问题及解决方式
- CAS:
-
package com.model.CAS; import java.util.concurrent.atomic.AtomicInteger; /** * @Description:测试类 * @Author: 张紫韩 * @Crete 2021/6/10 13:47 */ public class CASDemo { /** * 什么是CAS: compareAndSet 比较并交换 * 总结:比较当前工作内存的值和主内存的值,如果这个值是期望的,则执行操作,如果不是就一直循环(自旋锁) * 缺点: * 1.循环耗时 * 2.一次性只能保证一个共享变量的原子性 * 3.会产生ABA问题 * */ public static void main(String[] args) { //原子类 java.until.concurrent.atomic AtomicInteger atomicInteger = new AtomicInteger(2021); //expect :期望 update:更新 //如果是2021 就改为2022并返回true,如果不是2021就不会更新为2022并返回false ,CAS是cpu的原语 System.out.println(atomicInteger.compareAndSet(2021, 2022)); //返回值为ture和false System.out.println(atomicInteger.get()); System.out.println(atomicInteger.compareAndSet(2021, 2022)); System.out.println(atomicInteger.get()); atomicInteger.getAndIncrement(); // 相当于是++ /** * atomicInteger.getAndIncrement(); 是如何实现的呢? * * 通过源码我们可以看到,他是通过调用 unsafe.getAndAddInt(this, valueOffset, 1)来实现加一的, * 我们在进入到 unsafe类中,他调用了更底层的compareAndSwapInt(var1, var2, var5, var5 + var4))进行加一 * 而public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5) * 类是由native标识的,是调用了底层的c语言库 * * public final int getAndIncrement() { * return unsafe.getAndAddInt(this, valueOffset, 1); * } * * public final int getAndAddInt(Object var1, long var2, int var4) { * int var5; * do { * var5 = this.getIntVolatile(var1, var2); * } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); * * return var5; * } * * */ } }
-
- CAS带来的ABA问题:
-
package com.model.CAS; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicStampedReference; /** * @Description:测试类 * @Author: 张紫韩 * @Crete 2021/6/10 14:13 */ public class CAS_ABA { /** * 当一个线程去获取一个值得时候 :获取到之后 A=1,在执行 compareAndSet(1,2)之前 * 另一个线程也获取到了A=1 ,然后快速的执行 compareAndSet(1,2),和compareAndSet(2,1) * 这是 第一个线程在执行 compareAndSet(1,2)时 他拿到的A=1已经不是最新的值,已经被其他的线程修改了 * */ public static void main(String[] args) { //解决ABA问题,使用原子引用, AtomicInteger atomicInteger = new AtomicInteger(2021); // AtomicStampedReference<Integer> atomicInteger = new AtomicStampedReference<>(2020, 1); //捣乱的线程 new Thread(() ->{ System.out.println(Thread.currentThread().getName()+"获取到了"+atomicInteger.get()); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.print(Thread.currentThread().getName()+"修改了"+atomicInteger.compareAndSet(2021, 2022)); System.out.println(atomicInteger.get()); System.out.print(Thread.currentThread().getName()+"修改了"+atomicInteger.compareAndSet(2022, 2021)); System.out.println(atomicInteger.get()); System.out.println(Thread.currentThread().getName()+"修改完毕"+atomicInteger.get()); },"B").start(); new Thread(() ->{ System.out.println(Thread.currentThread().getName()+"获取到了"+atomicInteger.get()); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } //被骗的线程 System.out.println(Thread.currentThread().getName()+"修改了"+atomicInteger.compareAndSet(2021, 2022)); System.out.println(Thread.currentThread().getName()+"修改完毕"+atomicInteger.get()); },"A").start(); } }
- ABA问题解决,引用原子引用,
-
CAS:方法中会有比较,比较时的出现的坑 ,不能超出包装类的 赋值范围
-
package com.model.CAS; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicStampedReference; /** * @Description:测试类 * @Author: 张紫韩 * @Crete 2021/6/10 14:13 */ public class CAS_ABA_JJ { /** * 当一个线程去获取一个值得时候 :获取到之后 A=1,在执行 compareAndSet(1,2)之前 * 另一个线程也获取到了A=1 ,然后快速的执行 compareAndSet(1,2),和compareAndSet(2,1) * 这是 第一个线程在执行 compareAndSet(1,2)时 他拿到的A=1已经不是最新的值,已经被其他的线程修改了 * */ public static void main(String[] args) { //解决ABA问题,使用原子引用, //大坑:AtomicStampedReference包装类,注意,如果泛型是一个包装类,注意对象的引用问题 // integer使用了对象缓存机制,默认的范围是-128 - 127,推荐使用静态工厂模式的方法valueOf获取对象,而不是new ,因为valueOf使用了缓存, // 而new 一定会创建新的对象分配新的内存空间 AtomicStampedReference<Integer> atomicInteger = new AtomicStampedReference<>(1, 1); //捣乱的线程 new Thread(() ->{ int stamp=atomicInteger.getStamp(); //获得的版本号 System.out.println(Thread.currentThread().getName()+"获得版本号"+stamp); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.print(Thread.currentThread().getName()+"修改了"+atomicInteger.compareAndSet(1, 2,atomicInteger.getStamp(),atomicInteger.getStamp()+1)); System.out.println(Thread.currentThread().getName()+"修改完毕版本号是:"+atomicInteger.getStamp()); System.out.print(Thread.currentThread().getName()+"修改了"+atomicInteger.compareAndSet(2, 1,atomicInteger.getStamp(),atomicInteger.getStamp()+1)); System.out.println(Thread.currentThread().getName()+"修改完毕版本号是:"+atomicInteger.getStamp()); },"B").start(); new Thread(() ->{ System.out.println(Thread.currentThread().getName()+"获取到版本号"+atomicInteger.getStamp()); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } //被骗的线程 System.out.println(Thread.currentThread().getName()+"修改了"+atomicInteger.compareAndSet(2021, 2022,atomicInteger.getStamp(),atomicInteger.getStamp()+1)); System.out.println(Thread.currentThread().getName()+"修改完毕"+atomicInteger.getStamp()); },"A").start(); } }
-