《温故而知新系列》JAVA多线程之CAS底层原理

1.什么是CAS

CAS  的全称为Compare-And-Swap ,它是一条CPU并发原语。它的功能是判断内存某个位置的值是否为预期值,如果是则更改为新的值,这个过程是原子的。

就一句话 比较并交换  废话不多说上代码

 2.代码Demo验证

  static class CASDemo {


        public static void main(String[] args) throws InterruptedException {

             //主线程正常执行 compareAndSet比较并交换方法
           AtomicInteger atomicInteger =  new AtomicInteger(5);
            boolean b = atomicInteger.compareAndSet(5, 2019);
            System.out.println(b + "\t current data:" + atomicInteger.get());
            //休息3秒
            Thread.sleep(3000);

            //开个子线程取修改主内存变量 看看能否修改成功 compareAndSet比较并交换方法
            new Thread(() -> {
                boolean b1 = atomicInteger.compareAndSet(5, 2018);
                System.out.println(b1 + "\t current data:" + atomicInteger.get());
            }).start();

        }


    }

3.通过流程图解释原理

 t1线程的工作内存初始值拷贝主内存为5
调用compareAndSet方法修改工作内存值为2019
(修改之后会回写给主内存,并通知其他内存可见,一般加上volitaile关键字)在回写的时候就会判断主内存的值跟我之前传参数期望的值是否相等,
如果相等 那么好 就会正常回写并覆盖主内存值 ,反之值回写不成功

总之用人话说就是:::你期望的的值 与 实际主内存的值(就是线程共享变量初始化放在物理内存的值) 它俩是不是一样   一样就可以修改   不一样就改不了

2.CAS的底层原理?如果你知道谈谈UnSafe类的理解

 

 this: 当前原子类对象   offset:内存偏移量(当前对象内存地址)delta: 每次自增1

 在构建原子类对象的时候 已经给对应的值加上volitaile关键字 保证可见性了

// The following contain CAS-based Java implementations used on
    // platforms not supporting native instructions

    /**
     * Atomically adds the given value to the current value of a field
     * or array element within the given object <code>o</code>
     * at the given <code>offset</code>.
     *
     * @param o object/array to update the field/element in
     * @param offset field/element offset
     * @param delta the value to add
     * @return the previous value
     * @since 1.8
     */
    public final int getAndAddInt(Object o, long offset, int delta) {
        int v;
        do {
            v = getIntVolatile(o, offset);
        } while (!compareAndSwapInt(o, offset, v, v + delta));
        return v;
    }

通过 do  ..while 循环  至少循环一次 然后比较并交换

3.什么是Unsafe类

     是CAS的核心类,由于JAVA方法无法直接访问底层系统,需要通过本地(native)方法来访问,Unsafe相当于一个后门,基于该类可以直接操作特定的内存数据。Unsafe存在于sun.misc包中,其内部方法可以像C的指针一样直接操作内存,因为Java中CAS操作的执行依赖于Unsafe类的方法

注意Unsafe类中的所有方法都是native修饰的,也就是说Unsafe类中的方法都直接调用操作系统底层资源执行相应任务

4 变量 valueOffset :

    表示该变量在内存中的偏移地址,因为Unsafe就是根据内存偏移地址获取数据的

3.CAS缺点

    public final int getAndAddInt(Object o, long offset, int delta) {
        int v;
        do {
            v = getIntVolatile(o, offset);
        } while (!compareAndSwapInt(o, offset, v, v + delta));
        return v;
    }

1.如果CAS失败,会一直进行尝试。如果CAS长时间一直不成功,可能会给CPU带来很大的开销

2.只能保证一个共享变量的原子操作

3.引出来ABA问题?

4.ABA问题

ABA:狸猫换太子

CAS算法实现一个重要的前提需要取出内存中某时刻的数据在当下时刻比较并交换,那么在这个时间差 会导致数据的变化

通过原型图举例::

 解释当t2线程执行无数次之后最终写回主内存还是A,t1线程到时间执行发现主内存是A,,符合CAS算法
最后导致数据错误!!!!

废话不多说 代码演示ABA出现的问题::

static class ABADemo {

        static AtomicReference atomicReference = new AtomicReference(100);
        static AtomicStampedReference atomicStampedReference = new AtomicStampedReference<Integer>(100, 1);

        public static void main(String[] args) {
            System.out.println("======以下是ABA问题的产生============");
             new Thread(()->{
                 atomicReference.compareAndSet(100,101);
                 atomicReference.compareAndSet(101,100);
             },"t1").start();


            new Thread(()->{
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(atomicReference.compareAndSet(100, 2022)+"\t"+atomicReference.get());
            },"t2").start();

控制台输出:======以下是ABA问题的产生============

控制台输出:true    2022

5.解决ABA问题 

1.使用  带时间戳的原子引用类

AtomicStampedReference<Object> reference = new AtomicStampedReference<>();

废话不多说 上代码::

System.out.println("============以下是ABA问题的解决=================");

            new Thread(() -> {
                //初始版本号
                int stamp = atomicStampedReference.getStamp();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "\t第一次版本号:" + stamp);

                atomicStampedReference.compareAndSet(100, 101,
                        atomicStampedReference.getStamp(),
                        atomicStampedReference.getStamp() + 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();
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "\t第一次版本号:" + stamp);
                boolean b = atomicStampedReference.compareAndSet(100, 2022,
                        stamp, stamp + 1);
                System.out.println(Thread.currentThread().getName() + "\t线程修改是否成功" + b + "当前最新实际版本号"
                        + atomicStampedReference.getStamp());
                System.out.println("当前最新值" + atomicStampedReference.getReference());

            }, "t4").start();
        }


    }

控制台输出:============以下是ABA问题的解决=================

控制台输出:t3    第一次版本号:1

控制台输出:t3    第二次版本号:2

控制台输出:t3    第三次版本号:3

控制台输出:t4    第一次版本号:1

控制台输出:t4    线程修改是否成功false当前最新实际版本号3

控制台输出:当前最新值100

小结: 1 CAS比较并交换方法只能保证数据的稳定性 ,并不能保证原子性,原子性是实质

底层是通过JAVA提供的Unsafe类调用C语言外部的接口,底层是调用的汇编的一个原子指令给数据总线加锁所以线程是安全的的。

           2 CAS缺点通过 期望值和实际值是否相等 并不能保证中间有空档期  所以需要通过版本号来控制   JUC提供的AtomicStampedReference 时间戳原子类 保证了没改变一次主内存数据  版本号+1 在修改主内存数据时  除了通过值判断相等外还加上版本号预计值和实际值是否相等  双重控制  解决了ABA问题。

原创不易,有不对的地方希望大家指正 !!!!!!

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 1 University students can understand innovation through learning from the past. 2. Students can better review by breaking down complex concepts into smaller components and studying the material in an organized way. 3. When learning from the past to understand innovation, it is important to focus on understanding the big picture and to not get bogged down in the details. ### 回答2: 1. 大学生如何理解温故而知新温故而知新是一种学习方法,它要求我们在学习新知识之前先回顾和巩固已经学过的知识。大学生理解温故而知新意味着要在学习新知识之前,先回顾和复习以前学过的相关知识或基础知识。通过温故,我们能够加深对已有知识的理解和记忆,从而更好地理解和掌握新的知识。 2. 学生如何更好地去复习? 学生要更好地复习,可以采取以下策略: 首先,制定一个合理的复习计划,将要复习的内容分配到不同的时间段,确保每个科目都有足够的时间。 其次,采用多种复习方法,如阅读教材、做练习题、参加讨论等,以帮助加深理解和牢固记忆。 另外,与同学或老师一起讨论复习内容,通过讲解和互动来加深理解。 此外,保持良好的学习习惯,比如及时复习、做好笔记等,能够帮助学生更好地掌握和复习知识。 3. 温故而知新的过程需要注意什么? 在温故而知新的过程中,需要注意以下几点: 首先,要有针对性,根据自己的学习需求和复习目标,选择性地回顾和复习相关知识点。 其次,要有系统性,将复习内容进行分类整理,形成一个清晰的知识框架,有助于加深理解和记忆。 另外,要关注重难点,重点复习那些相对较难或容易遗忘的知识点,加强对这些内容的学习和理解。 还要有耐心和恒心,温故而知新是一个持续的过程,需要长期坚持和不断巩固。 最后,要善于总结和归纳,通过整理和回顾复习过程中的笔记和练习,提炼出关键概念和思维模式,便于记忆和应用。 ### 回答3: 1. 大学生如何理解温故而知新? 大学生可以理解为通过回顾过去的知识和经验,来获取新的见解和理解。温故是指回顾已经学过的知识,了解其中的原理、概念和重要点。而知新则是指通过对新知识的学习,扩展和更新自己的知识体系。温故而知新相辅相成,是一个持续学习和发展的过程。 2. 学生如何更好地去复习? 学生可以通过以下方式更好地进行复习: - 制定合理的复习计划:根据时间安排和课程难度,合理分配复习时间,确保每个学科都有足够的复习时间。 - 多种复习方法结合:采用不同的学习方式,如阅读教材、做练习题、参与讨论、制作思维导图等,帮助巩固记忆和理解知识。 - 主动参与课堂:积极参与讨论和提问,与同学和老师交流,加深对知识的理解和记忆。 - 不断反思和总结:及时检查自己的复习情况,发现不足和问题,并及时调整学习方法和计划。 3. 温故而知新的过程需要注意什么? 在温故而知新的过程中,学生需要注意以下几点: - 有目的性地温故:针对具体的知识点或者问题进行回顾,明确自己的学习目标和重点。 - 理解和记忆结合:不仅要理解概念和原理,还要通过多次的复习和记忆,帮助信息在大脑中形成长期记忆。 - 理论联系实际:将学到的知识应用到实际情境中,加深对知识的理解和记忆。 - 及时巩固复习成果:通过做练习题、整理笔记、与同学讨论等方式,巩固复习的成果,确保知识掌握得更牢固。 - 长期持续学习:温故而知新是一个持续的过程,要保持学习的热情和动力,不断更新自己的知识体系。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

森林里的程序猿猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值