Java中解决CAS机制出现的ABA问题

2023-10-27

Java中解决CAS机制出现的ABA问题?

1、先了解一下什么是CAS?

一句话总结就是: 比较并交换(compare and swap)是一条CPU并发原语
CAS的公式如下:

CAS(V,A,B)
1V 表示内存中的地址
2A 表示预期值
3B 表示要修改的新值

CAS的功能:就是预期值A与内存中的值相比较,如果相同则将内存中的值改变成新值B

2、CAS的底层原理?

换句话说也就是CAS为什么能保证原子性?
(1)靠的是底层的Unsafe类
(2)Unsafe类是CAS的核心类,由于java无法直接访问底层系统,需要通过本地(native)方法访问,Unsafe相当于一个后门,该类可以直接操作特定的内存数据。 Unsafe类存在于sun.misc包中,其内部方法操作可以像C的指针一样直接操作内存,因为java中的CAS依赖于Unsafe类中的方法
(3)注意Unsafe类中的所有方法都是native修饰的,也就是说Unsafe类中的方法都直接调用操作系统底昃资源执行相应任务,Unsafe类中的native方法是调用底层原语,原语是有原子性的

3、CAS的问题?

(1)循环时间长,开销比较大
CAS是Java乐观锁的一种实现机制,在Java并发包中,大部分类就是通过CAS机制实现的线程安全,它不会阻塞线程,如果更改失败则可以自旋重试,允许多线程并发修改,但是互相比较,互相比较以后直到全部的线程执行成功,并发性加强了,但是循环时间长,开销大。

解决办法:JVM支持处理器提供的pause指令,使得效率会有一定的提升,pause指令有两个作用:
(1)第一它可以延迟流水线执行指令,使CPU不会消耗过多的执行资源
(2)第二它可以避免在退出循环的时候因内存顺序冲突(memory order violation)
     而引起CPU流水线被清空(CPU pipeline flush),从而提高CPU的执行效率。

(2)只能保证一个共享变量的原子操作
对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁来保证原子性

解决办法:从Java1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,
        你可以把多个变量放在一个对象里来进行CAS操作。

(3)ABA问题
形象的说就是,狸猫换太子
ABA问题就是 讲桌上面放了1瓶水,张三有10秒的操作时间,他先把水换成了 水果,用了2秒,接着他又把水果换成了水,尽管最后的结果没有发生改变,但是这之间有很多次的操作机会,所以就造成了漏洞,也就是常说的狸猫换太子(比喻:你老婆出轨之后又回来,还是原来的老婆吗?)

4、怎么解决ABA问题?

AtomicReference原子引用
如果赋值操作不是线程安全的。若想不用锁来实现,可以用**AtomicReference**这个类,实现对象引用的原子更新

两种解决方法:

1)AtomicStampedReference 类:
    版本号原子引用,理解原子引用+新增一种机制,那就是修改版本号(类似时间戳)2)AtomicMarkableReference 类

(1)AtomicStampedReference 示例:
看示例代码之前,请先去看看AtomicStampedReference 方法的API可以帮助理解
链接: 点击这里查看方法各个参数的含义.
示例:

package com.song.test01;

import java.util.concurrent.atomic.AtomicStampedReference;


public class Test09 {
    public static void main(String[] args) {
        String str1 = "aaa";
        String str2 = "bbb";
        //1、传入初始引用 和 初始标志 :aaa 和 1
        AtomicStampedReference<String> reference = new AtomicStampedReference<String>(str1,1);
        System.out.println("reference.getReference() = " + reference.getReference()+",版本号为"+reference.getStamp());

        System.out.println("============================================");

        /*
        2、(1)第一步 判断 当前引用(也就是初始引用str1) 和 该引用的预期值str1是否相等    str1 == str1  所以相等
           (2)第二步 判断 当前标志(也就是上面的版本号1)和  预期标志reference.getStamp() 是否相等
           (3)如果上面两步都相等了,则以原子方式将该引用和该标志的值设置为给定的更新值
           (4)str2是新的值,reference.getStamp() + 1 是新的版本号
         */
        boolean b1 = reference.compareAndSet(str1, str2, reference.getStamp(), reference.getStamp() + 1);
        System.out.println("b1:"+b1);
        System.out.println("reference.getReference() = " + reference.getReference()+",版本号为"+reference.getStamp());

        System.out.println("===========================================");

        /* 3、 (1)如果当前引用 == 预期引用(str2),则以原子方式将该标志的值设置为给定的更新值
               (2)该标志的新值  reference.getStamp() + 1

         */
        boolean b2 = reference.attemptStamp(str2, reference.getStamp() + 1);
        System.out.println("b2: "+b2);
        //这里把版本号 改为 3
        System.out.println("reference.getStamp() = "+reference.getStamp());

        System.out.println("==========================================");

        /* 4、(1)和2步骤中基本像素
                 如果当前引用 == 预期引用,并且当前标志等于预期标志,则以原子方式将该引用和该标志的值设置为给定的更新值
              (2)这里虽然 当前引用(bbb)和预期引用(str2)相等,但是版本号不一致,当前版本号是3,预期版本号是4

         */
        boolean c = reference.weakCompareAndSet(str2,"ccc",4, reference.getStamp()+1);
        System.out.println("reference.getReference() = " + reference.getReference()+",版本号为"+reference.getStamp());
        System.out.println("c = " + c);

    }
}


输出结果:
在这里插入图片描述
通过stamp这个标记(版本号)属性来记录CAS每次设置值的操作,而下一次再CAS操作时,由于期望的stamp与现有的stamp不一样,因此就会设值失败,从而杜绝了ABA问题的复现

(2)AtomicMarkableReference
基本和AtomicStampedReference差不多,AtomicStampedReference主要关注版本号,即reference的值被修改了多少次;AtomicMarkableReference是使用boolean mark来标记reference是否被修改过

既然有了 AtomicStampedReference 为啥还需要再提供 AtomicMarkableReference 呢,在现实业务场景中,不关心引用变量被修改了几次,只是单纯的关心是否更改过

查看示例前可以看看 API,方便理解各个参数
链接: 点击这里查看方法的各个参数含义.

示例:

package com.song.test01;

import java.util.concurrent.atomic.AtomicMarkableReference;

public class Test10 {
    /**
     * initialRef- 初始参考 - cat
     * initialMark- 初始标记 - false
     */
    static AtomicMarkableReference<String> atomicStampedReference = new AtomicMarkableReference("cat",false);

    public static void main(String[] args) {
        // public boolean isMarked() 返回标记的当前值。
        boolean oldMarked = atomicStampedReference.isMarked();
        //public V getReference() 返回参考的当前值。
        String oldReference = atomicStampedReference.getReference();

        System.out.println("初始化之后的标记:"+oldMarked);
        System.out.println("初始化之后的值:"+oldReference);

        System.out.println("==============================================");

        String newReference = "dog";

        boolean b =atomicStampedReference.compareAndSet(oldReference,newReference,true,false);
        if(!b){
            System.out.println("Mark不一致,无法修改Reference的值");
        }
        b =atomicStampedReference.compareAndSet(oldReference,newReference,false,true);
        if(b){
            System.out.println("Mark一致,修改reference的值为dog");
        }
        System.out.println("修改成功之后的Mark:"+atomicStampedReference.isMarked());
        System.out.println("修改成功之后的值:"+atomicStampedReference.getReference());
    }
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java中解决CAS机制出现的ABA问题 的相关文章

随机推荐

  • Lua : 循环就三种,谁用谁知道

    Lua中的循环 分为while 循环 for循环和 repeat until 循环 语法比较简单 看看例子就秒懂了 while循环 while 循环 while condition do statement end local a 0 wh
  • android 仿ios右滑退出当前activity

    1 概述 iPhone的用户在使用APP的时候 打开很多页面想关闭的时候只需要在屏幕的左侧向右滑动即可退出当前页面 这是iPhone没有返回键的原因 但是这个效果很炫酷呀 能应用在Android上不是更好吗 今天就来讲一个这个功能的实现 不
  • 【C++】string

    C 修炼秘籍 string 目录 C 修炼秘籍 string 文章目录 前言 一 标准库里的string 二 string常用接口功能简介 具体使用和底层转到模拟实现 1 string类的常见构造函数 2 string类对象的容量操作 3
  • 【华为OD统一考试B卷

    华为OD统一考试A卷 B卷 新题库说明 2023年5月份 华为官方已经将的 2022 0223Q 1 2 3 4 统一修改为OD统一考试 A卷 和OD统一考试 B卷 你收到的链接上面会标注A卷还是B卷 请注意 根据反馈 目前大部分收到的都是
  • Vue中DOM的更新为什么是异步的?

    在 Vue 中 DOM 的更新是异步的机制是为了优化性能和提升用户体验 这个机制被称为 异步更新队列 Vue的异步更新队列机制是其实现高效渲染的关键 它通过将多次数据变化合并到一个批处理中 从而减少了不必要的DOM操作 提高了性能 下面是V
  • costmap 代价地图

    转自 https sychaichangkun gitbooks io ros tutorial icourse163 content chapter10 10 3 html 10 3 costmap costmap是Navigation
  • 嵩天老师的零基础Python笔记:https://www.bilibili.com/video/av13570243/?from=search&seid=15873837810484552531 中的...

    coding gbk 嵩天老师的零基础Python笔记 https www bilibili com video av13570243 from search seid 15873837810484552531 中的15 22讲 数字类型的
  • python批量发送带附件的邮件时,收到的附件名异常且后缀为bin,解决办法

    1 问题描述 在使用 Python 内置的模块 smtplib 发送带中文名的附件邮件时 数据可以正常传输 但是但收件人收到的附件格式是bin 附件名也不是发送时的名称 附注 smtp Simple Mail Transfer Protoc
  • 进一步理解angular

    在上一篇文章中我介绍了安装和结构 以及运行过程 https blog csdn net weixin 42603009 article details 94382945 我们进一步理解其中的数据单向 双向 组建传值 Modules等的理解
  • C语言编译过程、VIM常用命令

    一 编译过程 1 预处理 gcc E 源文件 c o 源文件 i 预处理宏以及注释 2 编译 gcc S 源文件 i o 源文件 s 通过编译转换为汇编文件 3 汇编 gcc c 源文件 s o 源文件 o 经汇编转换为二进制文件 4 链接
  • linux创建链接文件

    链接文件的创建 1 概念 链接文件就类似我们windows的快捷方式 只保留目标文件的地址 不占用存储空间 使用链接文件与使用目标文件的效果是一样的 2 为什么要使用链接文件 在windows都会把文件放在一个比较大的磁盘中 我们每次需要使
  • ChatGPT 速通手册——GPT 训练数据集介绍

    GPT 训练数据集介绍 所有人工智能算法都会分为训练和推理两步 算法的效果好坏 很大程度上取决于训练数据本身的质量 ChatGPT 所用的训练数据 openai 公司没有单独公布过细节 不过考虑到 ChatGPT 是在前序 GPT 算法基础
  • linux使用记录(一)

    1 tar 解压tar xvf file tar 解压 tar包 tar xzvf file tar gz 解压tar gz tar xjvf file tar bz2 解压 tar bz2 tar xZvf file tar Z 解压ta
  • 使用ADO.NET访问数据库

    一 ADO NET 数据库访问的方法和技术 二 ADO NEt的重要组件 1 DataSet 独立于数据源的数据访问 2 Net framework数据提供程序 用于连接到数据库执行命令和检索结果 三 NET数据提供程序的四个核心对象 1
  • 无向图——邻接表和邻接矩阵的实现

    邻接矩阵 include
  • 封装七牛云存储工具类

    文章目录 封装七牛云存储工具类 为啥选择七牛云 当然是因为它能免费使用喽 白嫖怪哈哈哈 图片存储方案 Java SDK操作七牛云 封装工具类 封装七牛云存储工具类 为啥选择七牛云 当然是因为它能免费使用喽 白嫖怪哈哈哈 图片存储方案 在实际
  • UnityShader——Compute Shader

    Compute Shader是基于DX11 SM4 5 的在GPU上运行的程序 通过Compute Shader我们可以将大量可以并行的计算放到GPU中计算从而节省CPU资源 Unity 5 6版本提供的 Graphics DrawMesh
  • U盘数据丢失是什么原因?轻松让U盘数据恢复的教程

    在数字化时代 我们不可避免地使用各种便携式存储设备 如U盘 来传输和存储重要数据 然而 有时我们可能不小心删除了U盘中的文件 或者格式化了U盘等等而导致数据丢失 这种情况下 你可能会困惑地想知道 是否有简单的方式可以快速而轻松地恢复U盘中丢
  • 解决报错:You don‘t have enough free space in /var/cache/apt/archives/.

    apt安装package时遇到问题 You don t have enough free space in var cache apt archives 问题 var 目录下空间不足 apt下载时没有地方存放缓存文件 解决方法 删除部分日志
  • Java中解决CAS机制出现的ABA问题

    Java中解决CAS机制出现的ABA问题 学习目标 Java中解决CAS机制出现的ABA问题 1 先了解一下什么是CAS 2 CAS的底层原理 3 CAS的问题 4 怎么解决ABA问题 1 先了解一下什么是CAS 一句话总结就是 比较并交换