CAS 与 ABA问题

news2024/9/22 19:34:11

本文通过学习:周阳老师-尚硅谷Java大厂面试题第二季 总结的CAS和ABA相关的笔记

一、CAS

1、CAS定义

CAS = Compare-And-Swap,它是CPU并发原语。

比较当前工作内存中的值和主物理内存中的值,如果相同则执行规定操作,否者继续比较直到主内存和工作内存的值一致为止。

3个操作数,内存值V,旧的预期值A,要修改的更新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否者什么都不做

2、CAS底层原理(unsafe类 + 变量valueOffset + 变量value用volatile修饰)

atomicInteger.getAndIncrement()

实际是调用了一个unsafe类的getAndAddInt方法

unsafe

变量valueOffset

变量value用volatile修饰

usafe

Unsafe是CAS的核心类,由于Java方法无法直接访问底层系统,需要通过本地(Native)方法来访问,Unsafe相当于一个后门,基于该类可以直接操作特定的内存数据。Unsafe类存在sun.misc包中,其内部方法操作可以像C的指针一样直接操作内存,因为Java中的CAS操作的执行依赖于Unsafe类的方法。注意Unsafe类的所有方法都是native修饰的,也就是说unsafe类中的方法都直接调用操作系统底层资源执行相应的任务。

为什么Atomic修饰的包装类?->能够保证原子性,依靠的就是底层的unsafe类。

valueOffset

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

通过valueOffset,直接通过内存地址,获取到值,然后进行加1的操作。

value用volatile修饰

volatile保证了多线程之间的内存可见性。

3、CAS优缺点

优点

  • CAS不加锁,在并发量不高时会提高效率。

缺点

  • 循环时间长,开销大因为执行的是do while,如果比较不成功一直在循环,最差的情况,就是某个线程一直取到的值和预期值都不一样,这样就会无限循环。

  • 只能保证一个共享变量的原子操作。当对一个共享变量执行操作时,我们可以通过循环CAS的方式来保证原子操作;但是对于多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候只能用锁来保证原子性。

  • ABA问题

4、synchronized与CAS区别

synchronized

(悲观锁)

CPU 悲观锁机制,即线程获得的是独占锁。独占锁就意味着 其他线程只能依靠阻塞来等待线程释放锁。而在 CPU 转换线程阻塞时会引起线程上下文切换,当有很多线程竞争锁的时候,会引起 CPU 频繁的上下文切换导致效率很低。尽管 Java1.6 为 synchronized 做了优化,增加了从偏向锁到轻量级锁再到重量级锁的过度,但是在最终转变为重量级锁之后,性能仍然较低。

CAS

(乐观锁)

乐观锁,它不会阻塞任何线程,所以在效率上,它会比 synchronized 要高。所谓乐观锁就是:每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。

二、ABA问题及解决

原子引用

结果:

true 2019

原子引用计数(解决ABA问题)

结果:

t3 第一次版本号1

t4 第一次版本号1

t3 第一次版本号2

t3 第一次版本号3

t4 修改成功否:false 当前最新实际版本号:3

t4 当前实际最新值100

public class ABADemo {
    //普通的原子引用包装类
    static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);

    public static void main(String[] args) {
        new Thread(() -> {
            // 把100 改成 101 然后在改成100,也就是ABA
            atomicReference.compareAndSet(100, 101);
            atomicReference.compareAndSet(101, 100);
        }, "t1").start();

        new Thread(() -> {
            try {
                // 睡眠一秒,保证t1线程,完成了ABA操作
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(atomicReference.compareAndSet(100, 2019) 
                        + "\t" + atomicReference.get());

        }, "t2").start();
    }
}
//true    2019
public class ABADemo {
    // 传递两个值,一个是初始值,一个是初始版本号
    static AtomicStampedReference<Integer> atomicStampedReference 
                    = new AtomicStampedReference<>(100, 1);

    public static void main(String[] args) {
        new Thread(() -> {
            threadName = Thread.currentThread().getName();
            int stamp1 = atomicStampedReference.getStamp();//获取版本号
            System.out.println(threadName + "\t 第一次版本号" + stamp1);
        
            // 传入4个值,期望值,更新值,期望版本号,更新版本号
            int stamp2 = atomicStampedReference.getStamp();
            atomicStampedReference.compareAndSet(100, 101, stamp2, stamp2+1);
            System.out.println(threadName + "\t 第二次版本号" + stamp2);

            int stamp3 = atomicStampedReference.getStamp();
            atomicStampedReference.compareAndSet(101, 100, stamp3, stamp3+1);
            System.out.println(threadName + "\t 第三次版本号" + stamp3);

        }, "t3").start();

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

            // 暂停3秒钟,保证t3线程也进行一次ABA问题
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            boolean result = atomicStampedReference.compareAndSet(100, 2019, stamp, stamp+1);
            int stamp2 = atomicStampedReference.getStamp();
            System.out.println(threadName + "\t 修改成功否:" + result + "\t 当前最新实际版本号:" + stamp2);
            System.out.println(threadName + "\t 当前实际最新值" + atomicStampedReference.getReference());
        }, "t4").start();
    }
}
/**
t3    第一次版本号1
t4    第一次版本号1
t3    第一次版本号2
t3    第一次版本号3
t4    修改成功否:false 当前最新实际版本号:3
t4    当前实际最新值100
*/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/392789.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【MySQL】第18章_MySQL8其它新特性

第18章_MySQL8其它新特性 1. MySQL8新特性概述 MySQL从5.7版本直接跳跃发布了8.0版本&#xff0c;可见这是一个令人兴奋的里程碑版本。MySQL 8版本在功能上做了显著的改进与增强&#xff0c;开发者对MySQL的源代码进行了重构&#xff0c;最突出的一点是多MySQL Optimizer优化器…

gitHub远程库

创建远程仓库注册一个gutHub账户点击号&#xff0c;在点击New repository新建一个远程仓库仓库名一般跟本地库的名称一致public公共的开源private私有的不公开远程仓库操作创建远程仓库别名基本语法git remote -v &#xff1a; 查看当前所有远程地址别名git remote add 别名 远…

ABAP ALV和OOALV设置单元格颜色,编辑

首先给大家分享一篇博客: REUSE_ALV_GRID_DISPLAY_LVC-可编辑单元格 文章目录单元格编辑单元格/行-颜色效果展示**需求:**我是想实现某个单元格可根据数据来判断是否是可以进行编辑的或要添加一个什么样的颜色. 我们需要用到下面的三个结构 ALV 控制: 单元格的类型表:LVC_T_ST…

Nios II软件开发流程简介(含工程)

软件安装 Nios II Eclipse软件打不开 ​ 安装完成quartus后&#xff0c;想要打开Nios II Eclipse软件&#xff0c;点击软件后发现没有任何反应。 ​ 这时要到安装目录C:\intelFPGA_pro\20.3\nios2eds\bin下&#xff0c;打开readme文件。 ​ 按readme中的内容下载eclipse-cpp-…

VTK中如何 搜索 目标点 最近的点或者点集( vtkPointLocator )

背景: 在vtk使用过程中,我们有时要搜索点或者cell最近的 单元, 仔细看源码,有时无法判断其具体是什么样子,因而这里做了可视化处理,方便我们更深刻的理解 vtkPointLocator 类型函数的使用; 过程: 1.了解其继承关系是必要的: 2.开始探索该函数的一些效果: 我们会将原始数据…

vmware安装redhat enterprise linux server 9.1

vmware安装redhat enterprise linux server 9.11、安装系统1.1 镜像文件2、更新系统2.1 注册系统到redhat软件仓库2.2 更新系统1、安装系统 1.1 镜像文件 官网下载&#xff1a;https://developers.redhat.com/products/rhel/download 2、更新系统 2.1 注册系统到redhat软件…

【编写中】html5+go+websocket不到150行代码,实现一个在线实时聊天的功能

阮一峰websocket 相关参考 websocket 什么是websocket 在了解什么是websocket之前&#xff0c;我们下说一说http&#xff0c;因为HTTP我们太熟了。我们知道&#xff0c;HTTP是一种基于应用层的网络协议&#xff0c;往往都是一个请求&#xff0c;一个相应。websocket呢&#…

为啥用 时序数据库 TSDB

前言 其实我之前是不太了解时序数据库以及它相关的机制的&#xff0c;只是大概知晓它的用途。但因为公司的业务需求&#xff0c;我意外参与并主导了公司内部开源时序数据库influxdb的引擎改造&#xff0c;所以我也就顺理成章的成为时序数据库“从业者”。 造飞机的人需要时刻…

VMware vCenter Server的安装和使用

准备工作 首先去官网下载好VCenter Server&#xff0c;然后准备安装&#xff0c;我这里下载的是6.0对应的镜像为VMware-VIMSetup-all-6.0.0-2656757.iso 需要注意&#xff1a; 开始安装 和安装其他操作系统一样&#xff0c;把镜像放入光驱或解压缩&#xff0c;我这里是在…

Dropout Reduces Underfitting论文解读

Dropout 在欠拟合的应用Dropout Reduces Underfitting&#xff08;2023.3.2&#xff09;写在前面摘要一、简介二、重新审视过拟合和欠拟合三、Dropout如何减少欠拟合四、方法五、实验早期随机失活分析晚期随机失活&#xff08;Late Dropout&#xff09;六、下游任务七、相关工作…

【零代码工具推荐】Max Creation Graph (MCG) 可视化图形编程工具

从3dMax 2016开始新加入了一个很牛great的功能&#xff0c;也就是“MCG”全称是Max Creation Graph&#xff0c;MCG可以让用户使用全可视化节点工作流程来创建修改器&#xff0c;几何体工具插件&#xff0c;使用MCG&#xff0c;可以创建一个新的插件&#xff0c;没错是插件&…

【大数据实时数据同步】超级详细的生产环境OGG(GoldenGate)12.2实时异构同步Oracle数据部署方案(下)

系列文章目录 【大数据实时数据同步】超级详细的生产环境OGG(GoldenGate)12.2实时异构同步Oracle数据部署方案(上) 【大数据实时数据同步】超级详细的生产环境OGG(GoldenGate)12.2实时异构同步Oracle数据部署方案(中) 【大数据实时数据同步】超级详细的生产环境OGG(GoldenGate…

要点提炼|《数字中国建设整体布局规划》,看这一篇就够了!

《数字中国建设整体布局规划》/// 近日&#xff0c;中共中央、国务院印发了《数字中国建设整体布局规划》&#xff08;以下简称“《规划》”&#xff09;&#xff0c;作为影响中国未来发展的重磅文件&#xff0c;被业界评价为“数字挂帅时代来临”。《数字中国建设整体布局规划…

【持续集成】Jenkins详细教程

文章目录一、jenkins是什么&#xff1f;二、CI/CD是什么&#xff1f;三、使用Jenkins进行PHP代码(单元)测试、打包。1.General2.源码管理3.构建触发器4.构建环境5.构建6.构建后操作7.其他相关配置四、进行jenkins project 构建五、构建结果说明六、jenkins权限管理最后&#xf…

【拼图】拼图游戏-微信小程序开发流程详解

还记得小时候玩过的经典拼图游戏吗&#xff0c;上小学时&#xff0c;在路边摊用买个玩具&#xff0c;是一个正方形盒子形状&#xff0c;里面装的是图片分割成的很多块&#xff0c;还差一块&#xff0c;怎么描述好呢&#xff0c;和魔方玩具差不多&#xff0c;有没有听说叫二维的…

【Leetcode——重排链表】

文章目录一、重排链表思路1.思路2.总结一、重排链表 对于这道题&#xff0c;有两种思路&#xff1a; 思路1. 1.使用一个线性表&#xff0c;存储链表中的每个节点&#xff0c;然后按照题目的条件&#xff0c;来链接线性表的各个节点即可。 使用左下标和右下标来定位线性表中的…

硬件学习 软件 Cadence day09 芯片PCB 封装导出DXF 文件

1.打开自己要导出 DXF 文件的 PCB 封装 (Allegro 软件) 2.导出DXF 文件的按钮 1.点击按钮&#xff0c;打开窗口 2.填写数据 3. 按下 Edit... 按钮 4. 编辑数据 5. 导出数据 &#xff0c;生成DXF 文件 下面的选项自己选择 &#xff1a; Color mapping &#xff1a; …

希腊字母及读音

希腊字母24个希腊字母分别是&#xff1a;Αα、Ββ、Γγ、Δδ、Εε、Ϝϝ、Ζζ、Ηη、Θθ、Ιι、Κκ、Λλ、Μμ、Νν、Ξξ、Οο、Ππ、Ρρ、Σσ、Ττ、Υυ、Φφ、Χχ、Ψψ、Ωω。拼写Α α&#xff1a;阿尔法 AlphaΒ β&#xff1a;贝塔 BetaΓ γ&…

算法套路二:相向双指针

算法套路二:相向双指针 算法套路示例讲解&#xff1a;LeetCode167. 两数之和 II - 输入有序数组 给你一个下标从 1 开始的整数数组 numbers &#xff0c;该数组已按 非递减顺序排列 &#xff0c;请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是…

如何在MacOS上卸载IPGuard的软件--LAgent/LSDhelper程序

IPGuard类的软件一般企业用于办公设备监控&#xff0c;获取员工在设备上操作的信息&#xff0c;同时对文件等信息加密&#xff0c;用于防止企业信息外泄到网络上。但是设备上安装了此类软件一般不容易卸载掉&#xff0c;针对在macos上卸载过程作下讲解。 1. 一般服务类的程序都…