Java CAS底层原理

Java CAS底层原理

Java CAS底层原理,这一篇就够了!!!

CAS全称(Conmpare And Swap)比较并交换,是一种用于在多线程环境下实现同步功能的机制。CAS 操作包含三个操作数 – 内存地址、预期值和新值。CAS 的实现逻辑是将内存地址的数值与预期数值想比较,若相等,则将内存位置处的值替换为新值。若不相等,则不做任何操作。

JAVA中CAS是通过自旋操作完成赋值,若值不相等再更新预期值、重新计算新值,接着进行CAS操作,直到成功为止。底层是JVM调用操作系统原语指令unsafe,并由CPU完成原子操作,你要知道并发/多线程环境下如果CPU没有原子操作我们是无法完成。

  • JAVA1.5开始引入了CAS,主要代码都放在JUC的atomic包下,如下图:
    在这里插入图片描述
  • JUC包下源码如下:
    在这里插入图片描述
  • 每一个操作都是调用unsafe方法实现结果。这是java自旋完成CAS源码:
    在这里插入图片描述

CAS优点

  • 没有引用锁的概念,并发量不高情况下提高效率
  • 减少线程上下文切换

CAS缺点

  • cpu开销大,在高并发下,许多线程,更新一变量,多次更新不成功,循环反复,给cpu带来大量压力。
  • 只是一个变量的原子性操作,不能保证代码块的原子性。
  • ABA问题

ABA问题

CAS带来最大问题就是ABA问题。有A、B两个线程,A线程运行10s,B线程运行2s,两个线程同一时间开始运行都修改同一变量m,假设m初始值为5,B线程修改m值5改为10,再修改m值10改为5。此时A线程修改m值5改为6,修改成功,满足CAS操作原理,而A线程认为m一直都是5,它并不知道m的值已经被修改过。

解决该问题通过添加版本号来解决,每次修改都带着本次修改的版本号,发现版本不是当前版本此修改失败,否则修改成功。如果自旋同理重新获得版本号计算旧值、新值等。

public class ABA {

    //ABA问题产生与解决
    static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);

    public static void main(String[] args) {
        //==========ABA问题的产生==========
        System.out.println("==========ABA问题的产生==========");

        new Thread(() -> {
            atomicReference.compareAndSet(100, 101);
            atomicReference.compareAndSet(101, 100);
        }, "t1").start();

        new Thread(() -> {
            //保证t1线程完成
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(atomicReference.compareAndSet(100, 2019) + "\t" + atomicReference.get());
        }, "t2").start();

        //暂停一会
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //==========ABA问题的解决==========
        System.out.println("==========ABA问题的解决==========");

        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "\t第一次版本号:" + stamp);

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            atomicStampedReference.compareAndSet(100, 101, stamp, stamp + 1);
            System.out.println(Thread.currentThread().getName() + "\t第二次版本号:" + atomicStampedReference.getStamp());
            atomicStampedReference.compareAndSet(101, 100, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() + "\t第三次版本号:" + atomicStampedReference.getStamp());
        }, "t3").start();

        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "\t第一次版本号:" + stamp);

            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            boolean result = atomicStampedReference.compareAndSet(100, 101, stamp, stamp + 1);
            System.out.println(Thread.currentThread().getName() + "\t修改成功否" + result + "\t当前最前版本号:" + atomicStampedReference.getStamp());
            System.out.println(Thread.currentThread().getName() + "\t当前实际值:" + atomicStampedReference.getReference());
        }, "t4").start();
    }
}

  • 运行结果:
    在这里插入图片描述
  • 5
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值