关于CAS

本文详细解释了CAS(CompareAndSwap)原理,展示了其在Java中如何通过原子类实现自增操作,以及如何通过自旋锁解决线程同步问题。同时讨论了ABA问题及其解决方案,强调了版本号在避免这类问题中的关键作用。
摘要由CSDN通过智能技术生成

什么是CAS:

CAS:Compare And Swap,比较且交换。

CAS中有三个参数:1.内存中原数据的值V  2.预期值A    3.修改后的数据B

Compare:V与A会先比较是否一样

Swap:如果V与A一致,那么就将B写入V

返回操作是否成功

伪代码:

public boolean CAS(int address,int expectValue,int swapValue){
        while(address == expectValue){
            address = swapValue;
            return true;
        }
        return false;
    }

值得注意的是:CAS并不是靠一段代码实现的,它其实是cpu里的一条指令完成的,且操作是原子性的,这就在一定程度上解决了线程安全的问题,故在以往加锁的基础上,又有一个新的选择来规避线程安全问题了


原子类:Atomic

自增操作伪代码:

class MyAtomicInteger{
    int value;

    public int getAndIncrement(){
        int oldValue = value;
        while(CAS(value,oldValue,oldValue+1) != true){
            oldValue = value;
        }
        //后置++  故不是返回+1后的值
        return oldValue;
    }
}

Java标准库中的atomic下的原子类,就是通过CAS来完成自增自减的操作的,此时不需要加锁,也是线程安全的

 public static void main(String[] args) throws InterruptedException {
        //原子类 基于CAS完成自增自减的操作 不用加锁 也是线程完全的
        AtomicInteger count = new AtomicInteger(0);

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

        Thread t2 = new Thread(() -> {
            for(int i = 0; i < 50000; i++){
                count.getAndIncrement(); //count++
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(count);
    }

我们来查看一下自增操作方法的源码:

 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;
    }

 public native int getIntVolatile(Object var1, long var2);

ps:native表示的是本地方法,其实现细节内容是由c++写的虚拟机中实现的

代码中的compareAndSwapInt就是我们上面所说的CAS操作了


自旋锁:

伪代码

public class mySpinLock {
    //自旋锁实现伪代码
    private Thread owner = null;

    public void lock(){
        while(!CAS(this.owner,null,Thread.currentThread())){
            //通过CAS可以知道当前锁是否被线程所有
            //如果此时锁已经被其他线程所有,那么此线程就会自旋等待CAS = false
            //如果此时锁资源是空闲的,那么就会owner设置为当前尝试加锁的线程
        }
    }

    public void unlock(){
        this.owner = null;
    }
}

CAS:检查当前的owner是否为null,为null就交换,将当前线程引用赋值给owner,循环结束,加锁完成,反之就会返回false,继续循环执行


ABA问题:

想象一个极端场景,我们在银行取钱的时候。使用CAS操作,此时有两个线程接收请求。

线程A:如果我有1000元,我取出500块钱,此时余额还是500(这时线程B阻塞)

再我第二次再准备取出五百前,有朋友又给我转了500,此时我的余额又变成了1000。

这个时候线程B开始运行(线程A阻塞):发现余额还是1000,那么久又会触发CAS操作,又给我扣了500块钱,此时卡里的余额只剩500了

这就是ABA的典型场景:值A被修改成了B  值被又被修改成了A   此时此A非彼A了

解决:添加一个版本号或者使用时间戳来判断当前版本,每次修改版本号+1。此时CAS的基准就变成了版本号,就非数据金额了(版本号没有改变,数据就没有被修改)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值