CAS/ABA问题

CAS

CAS的全称是Compare and Swap,一个CAS操作涉及到以下过程:

我们假设内存中的原数据V,旧的预期值A,需要修改的新值B。

1.比较A和V的值是否相等。

2.如果相等,则交换B和V的值。

3.返回交换操作是否成功。


CAS的应用

1.实现原子类

public class Demo_twentyfive {
    public static void main(String[] args) throws InterruptedException {
        AtomicInteger atomicInteger=new AtomicInteger();
        //相当于i++操作
        atomicInteger.getAndIncrement();
    }
}

通过形如上述代码就可以实现一个原子类. 不需要使用重量级锁, 就可以高效的完成多线程的自增操作.如下面代码所示:

public class Demo_twentyone {

    //public static int count=0;
    public static AtomicInteger count=new AtomicInteger();

    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->{
            for (int i = 0; i < 50000; i++) {
                //count++;
                count.getAndIncrement();

            }
        });

        Thread t2=new Thread(()->{
            for (int i = 0; i < 50000; i++) {
                //count++;
              count.getAndIncrement();
            }
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(count);
    }
}

2.实现自旋锁

实现方式如下面伪代码所示:

public class SpinLock {
  private Thread owner = null;
  public void lock(){
    // 通过 CAS 看当前锁是否被某个线程持有.
    // 如果这个锁已经被别的线程持有, 那么就自旋等待.
    // 如果这个锁没有被别的线程持有, 那么就把 owner 设为当前尝试加锁的线程.
    while(!CAS(this.owner, null, Thread.currentThread())){
   }
 }
  public void unlock (){
    this.owner = null;
 }
}

CAS中的ABA问题

ABA问题:

假设存在两个线程 t1 和 t2. 有一个共享变量 num, 初始值为 A.
接下来, 线程 t1 想使用 CAS 把 num 值改成 Z, 那么就需要

1.先把num的值读取到oldnum中。

2.比较oldnum的值是否和num相同

3.如果相等,则把Z赋值给num

但是在线程t1执行这个操作,线程t2可能在中途修改了num的值为B,然后又重新修改为了A,因此t1无法区分这个num始终是A还是中间发生了变化,这就是ABA问题。


ABA问题产生的BUG

通常情况下,ABA问题并不会产生BUG,但总有例外,下面以银行取钱的例子来说明。

假设存款中有100元,要取走50元。由线程1和线程2两个线程操作。

1.线程1 获取到当前存款值为 100, 期望更新为 50; 线程2 获取到当前存款值为 100, 期
望更新为 50.

2.线程1 执行扣款成功, 存款被改成 50. 线程2 阻塞等待中.

3.在线程2 执行之前, 突然又进账了50元。

4.线程2获取当前存款为100,符合预期,再扣50元,于是就产生了BUG,多扣了50元。

解决方案

给要修改的值, 引入版本号. 在 CAS 比较数据当前值和旧值的同时, 也要比较版本号是否符合预期.
CAS 操作在读取旧值的同时, 也要读取版本号。
真正修改的时候:
1.如果当前版本号和读到的版本号相同, 则修改数据, 并把版本号 + 1。
2.如果当前版本号高于读到的版本号. 就操作失败。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值