首页 > 其他分享 >2022.8.21 CAS与原子引用(解决CAS的ABA问题)

2022.8.21 CAS与原子引用(解决CAS的ABA问题)

时间:2022-08-21 23:45:27浏览次数:55  
标签:ABA 21 CAS System atomicInteger compareAndSet println out

19、深入理解CAS

什么是CAS

 package com.xing.cas;
 ​
 import java.util.concurrent.atomic.AtomicInteger;
 // 原子类的底层用的cas
 public class CASDemo {
     //compareAndSet比较并交换
     public static void main(String[] args) {
         AtomicInteger atomicInteger = new AtomicInteger(2020);//原子类
         /*
                                                 期望         更新
         public final boolean compareAndSet(int expect, int update) {
         return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
            }
          */
         //如果和期望的值相同就更新,CAS是CPU的并发原语
         System.out.println(atomicInteger.compareAndSet(2020, 2021));
         System.out.println(atomicInteger.get()); // 2021
 ​
         // 上面已经修改为了2021 所以这个失败
         System.out.println(atomicInteger.compareAndSet(2020, 20201));
         System.out.println(atomicInteger.get());//2021
    }
 }
 ​
 ​

AtomicInteger的getAndIncrement方法源码

 

 

unsafe类

 

 

精髓!!!!:var1对象等于var2内存地址偏移值,对应如果这个值还是我们希望的var5,那么我们就让这个值加一

总结:CAS: 比较当前工作内存中的值,若果这个值是期望中的,那么执行该操作 ,如果不是会一直循环因为底层是do while会一直循环

缺点:

  • 循环耗时

  • 一次性只能保证一个共享变量的共享性

  • ABA问题

ABA问题(狸猫换太子)别人改了又改回去,但是使用的人不知道

 

 

 package com.xing.cas;
 ​
 import java.util.concurrent.atomic.AtomicInteger;
 ​
 public class CASDemo {
     //比较并交换
     public static void main(String[] args) {
         AtomicInteger atomicInteger = new AtomicInteger(2020);//原子类
         /*
                                                 期望         更新
         public final boolean compareAndSet(int expect, int update) {
         return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
            }
          */
         //对我们写平时写的sql:乐观锁
         //如果和期望的值相同就更新,CAS是CPU的并发原语
         //*******捣乱的线程**********
         System.out.println(atomicInteger.compareAndSet(2020, 2021));
         System.out.println(atomicInteger.get()); // 2021
         System.out.println(atomicInteger.compareAndSet(2021, 2020));
         System.out.println(atomicInteger.get()); // 2020
         //********期望的线程*********
         System.out.println(atomicInteger.compareAndSet(2020, 2021));
         System.out.println(atomicInteger.get());//2021
    }
 }
 ​

 

 

虽然最终期望确实完成可但是我们不希望数据被人动过了还不知道下面来解决这个问题

如何解决这个问题?原子引用

我们使用另外一个 AtomicStampedReference 原子类 带有版本号时间戳,可以每次记录加一类似于乐观锁

20、原子引用(解决CAS的ABA问题)

带版本号的原子操作

 package com.xing.cas;
 ​
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicStampedReference;
 ​
 public class CASDemo {
     //比较并交换
     public static void main(String[] args) {
 ​
         //注意,如果泛型是一个包装类,注意对象的引用问题 正常业务中,泛型中都是对象
         //Integer 使用了对象缓存机制,默认范围是 -128 ~ 127 ,推荐使用静态工厂方法 valueOf 获取对象实例,而不是 new,因为 valueOf 使用缓存,而 new 一定会创建新的对象分配新的内存空间;
         AtomicStampedReference<Integer> atomicInteger = new AtomicStampedReference<>(1,1);//初始值,版本号时间戳
         new Thread(()->{
            int stamp = atomicInteger.getStamp();//获得版本号
            System.out.println("A = >" + stamp);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //最后两个参数,拿到最新的版本号,把版本号+1                         期望值           更新值               期望时间戳             新时间戳
            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();
 ​
 ​
         new Thread(()->{
             int stamp = atomicInteger.getStamp();//获得版本号
             System.out.println("B = >" + stamp);
 ​
             try {
                 TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                 e.printStackTrace();
            }
             System.out.println(atomicInteger.compareAndSet(1, 3, stamp, stamp + 1));
 ​
             System.out.println("b2 = >"  +atomicInteger.getStamp());
        },"b").start();
    }
 }
 ​

 

 

注意:Integer 使用了对象缓存机制,默认范围是 -128 ~ 127 ,推荐使用静态工厂方法 valueOf 获取对象实例,而不是 new,因为 valueOf 使用缓存,而 new 一定会创建新的对象分配新的内存空间;每一次的2020都是一个新的值

阿里巴巴手册

 

标签:ABA,21,CAS,System,atomicInteger,compareAndSet,println,out
From: https://www.cnblogs.com/shanzha/p/16611417.html

相关文章

  • 2022.8.21 各种锁理解
    21、各种锁理解1、公平锁和非公平锁:公平锁:非常公平,不能够插队,必须先来后到!FIFO非公平锁:非常不公平,可以插队(默认都是非公平)2、可重入锁递归锁  可重入锁sync......
  • 脚本技巧-case语句
    目录前言选项前言我们在写脚本时,经常遇到,需要复用脚本功能的情况,如根据参数,决定要指定执行哪部分功能,这里我们使用shell的case语句实现。选项case$parainmode1)p......
  • 8.21
    ABC249G题意:给定\(n\)张牌,每张牌正面是\(a_i\),背面是\(b_i\),要求从里面任选最少一张牌,使得证明的数字异或和不超过\(m\)的同时,背面数字异或和最大。\(n\leq2000,m,a_i,......
  • 2022.8.21 多校周报
    总结牛客第九场A一眼看出是尺取法,就A了。B一道很简单的概率dp,状态和转移方程都写出来了,但想着搞前缀和优化,没想到差分,就卡死了,有点可惜。G马拉车加哈希,但卡了除了双......
  • 2022.8.21 线程池
    11、线程池(重点)线程池Executors:3大方法、7大参数、4种拒绝策略池化技术程序的运行,本质:占用系统的资源!优化资源的使用!==>引进了一种技术池化池线程池、连接池、内......
  • 2022.8.21 四大函数式接口与Stream流式计算
    12、四大函数式接口(重点)   函数接口:只有一个方法的接口    @FunctionalInterface publicinterfaceRunnable{     publicabstractvoidrun(......
  • 2022.8.21 Forkjoin与异步回调
    14、Forkjoin(分支合并)什么是ForkJoinForkJoin在JDK1.7, 并行执行任务!提高效率。在大数据量中!大数据:MapReduce(把大任务拆分为小任务)Forkjoin特点:工作窃取,这里......
  • 2022.8.21 读写锁与阻塞队列
    9、读写锁   自定义的缓存,没有加锁,就会出现一个没有写入完成,另一个突然插进来的情况 packagecom.xing.rw; ​ importjava.util.HashMap; importjava.util.......
  • 2022.8.21 JUC
    1、什么是JUC1、什么是juc(学习方法:官方文档+源码)   JUC——(java.util.concurrent)是一个包名的缩写,java工具类下的一个并发功能的包。该包下存放的均为多线程相......
  • 2022-08-21 假突破立即做空,就如同突破收敛三角就做单一样,总会给你一段利润。
     同样是假突破,第二次上冲。左边完成了整个4h标准中枢,右边又形成了30分钟的笔中枢扩展。左边4h图,假突破之后,完成了4h一个全部线段中枢的上涨。最后一段是背驰段,结束了30......