java CAS原理分析和ABA问题的产生和解决方案

本文会从java的源码对CAS算法进行分析,分析出CAS底层实现和并发包的原子类操作用CAS而不用synchronized,此外还会分析CAS的缺点和ABA问题的解决方案,希望能够对大家有所帮助

1.什么是CAS

CAS 全称是 compare and swap,是一种用于在多线程环境下实现同步功能的机制。CAS 操作包含三个操作数 -- 内存位置、预期数值和新值。CAS 的实现逻辑是将内存位置处的数值与预期数值想比较,若相等,则将内存位置处的值替换为新值。若不相等,则不做任何操作。(更深层次的理解需要依靠汇编进行解释)

2.通过一个多线程累加一个变量案例来引入CAS

/**
 * 对一个变量进行累加
 */
public class CASDemo {

  
    public void add(){
        nummber++;
    }
    public void atomicAdd(){
        atomicInteger.getAndIncrement();
    }

    public static void main(String[] args) {

        ShareData shareData = new ShareData();
        for (int i = 0; i <20 ; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                   shareData.add();
                   shareData.atomicAdd();
                }
            },String.valueOf(i)).start();
        }
        while (Thread.activeCount()>2){
            Thread.yield(); //会使这个线程由正在运行的running状态转变为等待cpu时间片的runable状态。
        }
        System.out.println(shareData.nummber);  //结果小于等于20000
       System.out.println(shareData.atomicInteger.get()); //结果的每次都等于20000
    }
}
class ShareData{
    volatile int nummber =0;
    public void add(){
        nummber++;
    }

    AtomicInteger atomicInteger = new AtomicInteger(0);
    public void atomicAdd(){
        atomicInteger.getAndIncrement();
    }
}
通过这个案例,我们能够发现,对于一个int型的number就行++操作是线程不安全的,想详细了解,请看我的volatile 这篇文章链接如下:https://blog.csdn.net/oldshaui/article/details/89342858 ,但是使用JUC下面的AtomicInteger这个类就不会有问题,下面我们来看下AtomicInteger类的底层实现

我们来主要分析下getAndAddInt这个方法(白话分析):首先会执行do里面的数据,根据当前对象和内存地址,取到主存里面的值,进入while循环,里面的compareAndSwapInt是一个Native方法,由C++编写的,利用JNI调用的,这个就是CAS算法的核心,大致可以理解为,根据当前对象和内存地址,拿到主存的值,和当前线程的存储的值进行比较,如果一样,就进行更新,返回true,取反为false,跳出while循环,否则为false,取反为true,一直在while循环等待

为什么atomicInteger底层用CAS,而不用synchronize

CAS算法没有加锁,也可以保证线程安全,可以保证并发,synchronize只能保证线程安全,不能保证并发,加锁就会让程序串行化

CAS的缺点:

  1. 循环时间太长,我们看CAS的底层源码可以发现,程序可能一直都在while循环里面循环,知道等到主存的值和工作内存中的值一样,才可以跳出循环
  2. 只能保证一个共享变量的原子操作
  3. 引发ABA问题

 

什么是ABA问题

 白话解释:我们已经理解了CAS算法,比如有两个线程A,B,线程B执行的速度非常快,线程B把初始值100改为101,又把101改为100,如果此时线程A进行更新时,就会发现,他的本地缓存值和主存的值一样,他就会认为这个值没有被改变过,这个过程就是ABA问题,我们可以发现这过程中存在些猫腻

 ABA的解决方案:利用类似数据库乐观锁的机制,把每次更新操作都对应一个版本号,线程A去更新的时候,不光要判断当前线程的缓存值和主存的值是否一样,还要判断他拿到的版本号是否一致,AtomicStampedReference可以用来解决这个问题

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值