ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CAS如何解决ABA问题

2021-06-27 21:35:05  阅读:158  来源: 互联网

标签:ABA atomicStampedRef CAS 版本号 线程 compareAndSet 解决 new 100


点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人。

文章不定期同步公众号,还有各种一线大厂面试原题、我的学习系列笔记。

CAS如何解决ABA问题

什么是ABA:在CAS过程中,线程1、线程2分别从内存中拿到了当前值为A,,同时线程2把当前值A改为B,又把B该回来变为A,此后线程1拿到的仍为A,导致线程1执行cas成功,但这个过程却发生了ABA问题,现场资源可能和当初不一样了(线程2把当前值由A->B->A)

解决方法版本号机制,利用版本号标记线程1拿到的‘当前值’的版本,若线程2进行了A->B->A操作,则版本号会改变,那线程1再次拿到的‘当前值’的版本和第一次的肯定是不同的,从而判定cas失败;

java代码中AtomicStampedReference类的cas方法实现了版本号机制,可用它来解决ABA问题:

/**
 * @Description 测试解决CAS中的ABA问题
 * @Author afei
 * @date:2021/6/27
 */
public class AtomicStampedReferenceTest {
    //AtomicInteger和AtomicStampedReference的初始值都为100,但AtomicStampedReference带了版本号0
    private static AtomicInteger atomicInt = new AtomicInteger(100);
    private static AtomicStampedReference atomicStampedRef = new AtomicStampedReference(100, 0);
    public static void main(String[] args) {
        //会产生ABA问题
        aba1();
        //用AtomicStampedReference解决cas过程中的ABA问题
        aba2();
 }

 public static void aba1(){
     Thread t1 = new Thread(new Runnable() {
         @Override
         public void run() {
             boolean c3 = atomicInt.compareAndSet(100, 101);
             System.out.println(c3); // true
         }
     });
     Thread t2 = new Thread(new Runnable() {
         @Override
         public void run() {
             //t2改变100->101->100,compareAndSet的参数:期待的值,新值
             atomicInt.compareAndSet(100, 101);
             atomicInt.compareAndSet(101, 100);
         }
     });
     t1.start();
     t2.start();
     try {
         //t1,t2执行完,主线程才能继续执行
         t1.join();
         t2.join();
     } catch (InterruptedException e) {
         e.printStackTrace();
     }
 }
  public static void aba2(){
      Thread t1 = new Thread(new Runnable() {
          @Override
          public void run() {
              int stamp = atomicStampedRef.getStamp();
              try {
                  TimeUnit.SECONDS.sleep(2);
              } catch (InterruptedException e) {
              }
              //compareAndSet中四个参数分别为:期待的值,新值,期待的版本号,新的版本号
              boolean c3 = atomicStampedRef.compareAndSet(100, 101, stamp, stamp + 1);
              System.out.println(c3); // false
          }
      });
      Thread t2 = new Thread(new Runnable() {
          @Override
          public void run(){
              try {
                  TimeUnit.SECONDS.sleep(1);
              } catch (InterruptedException e) {
              }
              //t2改变100->101->100,compareAndSet中四个参数分别为:期待的值,新值,期待的版本号,新的版本号
              atomicStampedRef.compareAndSet(100, 101, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1);
              atomicStampedRef.compareAndSet(101, 100, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1);
          }
      });
      t1.start();
      t2.start();
  }
}

AtomicInteger的原理

Java可以利用Syschronized、ReentrantLock、AtomicInteger类实现线程安全,AtomicInteger封装了一个【volatile int value】属性,它可以对这个属性进行许多原子性操作,这些原子性操作大多是基于cas原理,而在cas中,AtomicInteger使用的是一个叫Unsafe的类中的方法,Unsafe可以提供一些底层操作,也就是CPU特定的指令集,进而避免了并发问题(Unsafe是一个很危险的类,它可以做一些和内存相关的操作)

OK,如果文章哪里有错误或不足,欢迎各位留言。
创作不易,各位的「三连」是二少创作的最大动力!我们下期见!

标签:ABA,atomicStampedRef,CAS,版本号,线程,compareAndSet,解决,new,100
来源: https://www.cnblogs.com/mofes/p/14942198.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有