ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

面试篇 —— 谈谈你对CAS的理解

2021-07-19 17:31:50  阅读:206  来源: 互联网

标签:版本号 CAS AtomicInteger 原子 面试 谈谈 线程 new


一、CAS是什么

比较后交换,为了保证原子性而进行的比较和交换。

二、CAS的使用

前面说到volatile关键字是不保证原子性的,为了满足轻量级的JMM原则,可以通过volatile + CAS实现轻量级的JMM原则(保证数据可见性、保证原子性、禁止指令重排以保证有序性原则)。例如:

public class VolatileDemo {
    // 定义volatile修饰的原子包装类
    public volatile AtomicInteger number = new AtomicInteger();
    //  实现原子整型包装类的自增i++
    public void atomicDemo() {
        number.getAndIncrement();
    }
}
    /**
     * 测试20个线程各执行1000次自增后结果
     */
    @Test
    public void atomicDemo() {
        VolatileDemo volatileDemo = new VolatileDemo();
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    volatileDemo.atomicDemo();
                }
            }, String.valueOf(i)).start();
        }
        // 等自建的所有线程执行完成后再执行以下代码,因为程序默认存在main线程和GC垃圾回收线程, 故>2
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println(volatileDemo.number.get());
    }

三、CAS原理

在AtomicInteger原子整型包装类方法中,使用的关键class是Unsafe,这个类中的方法是由native修饰的,这是jdk与计算机系统之间数据操作的约定后门,类比Thread中的start方法的底层。这些类存在于jdk本身自带的rt.jar中。

四、CAS缺点

在AtomicInteger中存在方法compareAndSet

    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

参数expect是第一次取出的数值(线程从主内存copy到自己的工作内存中),update是在进行数据操作后的数值。当主内存中的数值与expect相同,则将主内存中的数值更新为update数值,并通知其他线程保证可见性,如果不同则不进行更新。这样只关注取出和结果比对的过程,有一个明显的缺陷,那就是ABA问题,ABA问题就是取出时是A,中间不管被其他线程更新多少次,只要在当前线程结果比对之前再变为A,那么就算比对成功,当前线程就还会将其数值认为一直没有变化,对其进行更新。

五、CAS实例

除了AtomicInteger之外,jdk还提供了不同类型的原子型包装类

// 整型原子包装类,默认为0
AtomicInteger atomicInteger = new AtomicInteger();
// 布尔原子包装类,默认返回false
AtomicBoolean atomicBoolean = new AtomicBoolean();
// 自定义类原子包装
AtomicReference<Object> atomicReference = new AtomicReference<>();

AtomicLong atomicLong = new AtomicLong();
// 带有版本号的自定义原子包装类,初始化需要提供版本号
AtomicStampedReference<Object> atomicStampedReference = new AtomicStampedReference<>(null, 1);

六、ABA问题的解决

使用AtomicStampedReference自定义版本原子包装类可以解决ABA问题。原理是,在存到主内存时定义一个stamped版本号,copy值到线程工作内存时连带着版本号,而主内存中的值每更新一次,版本号也跟着自增。当前线程操作完值后进行CAS(比较并交换),此时需要比较值和版本号是否都一致,这样就避免了中间过程中值更新问题。

标签:版本号,CAS,AtomicInteger,原子,面试,谈谈,线程,new
来源: https://www.cnblogs.com/zzb-yp/p/15031231.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有