Java实现CAS的原理

news2024/11/20 4:52:15

文章目录

  • 1、 什么是CAS
  • 2、CAS的原理
  • 3、CAS的应用场景
  • 4、Java中的CAS实现
  • 5、使用AtomicInteger实现线程安全的计数器
  • 6、CAS实现原子操作的三大问题
    • 6.1、ABA问题
    • 6.2、循环时间长
    • 6.3、只能保证一个共享变量的原子性
  • 7、总结

1、 什么是CAS

CAS(Compare and Swap)是一种并发编程中的技术,用于实现多线程之间的原子操作。它允许你比较一个内存位置的值和一个预期的值,如果相等,则将新值设置到该内存位置,从而实现原子性的修改操作。CAS是乐观锁,线程执行的时候不会加锁,假设没有冲突去完成某项操作,如果因为冲突失败了就重试,最后直到成功为止。

2、CAS的原理

CAS操作主要包括两个步骤:

比较:首先,将内存位置的当前值与预期值进行比较。
交换:如果当前值与预期值相等,就将新值设置到内存位置。
在这里插入图片描述
前面提到,CAS是一种原子操作。那么Java是怎样来使用CAS的呢?我们知道,在Java中,如果一个方法是native的,那Java就不负责具体实现它,而是交给底层的JVM使用c或者c++去实现。

在Java中,有一个Unsafe类,它在sun.misc包中。它里面是一些native方法,其中就有几个关于CAS的:

boolean compareAndSwapObject(Object o, long offset,Object expected, Object x);
boolean compareAndSwapInt(Object o, long offset,int expected,int x);
boolean compareAndSwapLong(Object o, long offset,long expected,long x);

当然,他们都是public native的。

Unsafe中对CAS的实现是C++写的,它的具体实现和操作系统、CPU都有关系。

Linux的X86下主要是通过cmpxchgl这个指令在CPU级完成CAS操作的,但在多处理器情况下必须使用lock指令加锁来完成。当然不同的操作系统和处理器的实现会有所不同,大家可以自行了解。

当然,Unsafe类里面还有其它方法用于不同的用途。比如支持线程挂起和恢复的park和unpark, LockSupport类底层就是调用了这两个方法。还有支持反射操作的allocateInstance()方法。

3、CAS的应用场景

线程安全问题:CAS可以用于解决多线程环境下的数据竞争问题,如计数器、标志位等。
性能优化:CAS避免了使用锁的开销,适用于一些高并发、频繁修改的场景。

4、Java中的CAS实现

Java中提供了java.util.concurrent.atomic包,包含了一系列基于CAS的原子类,如AtomicInteger、AtomicLong等。

5、使用AtomicInteger实现线程安全的计数器

import java.util.concurrent.atomic.AtomicInteger;

public class Counter {
    private AtomicInteger value = new AtomicInteger(0);

    public void increment() {
        value.incrementAndGet();
    }

    public int getValue() {
        return value.get();
    }
}

为什么AtomicInteger就是原子性的操作,这里我们以AtomicInteger类的getAndAdd(int delta)方法为例,来看看Java是如何实现原子操作的。

public final int getAndAdd(int delta) {
    return U.getAndAddInt(this, VALUE, delta);
}

这里的U其实就是Unsafe对象;

6、CAS实现原子操作的三大问题

尽管CAS在很多情况下是非常有用的,但它也存在三大经典问题:ABA问题、循环时间长以及只能保证一个共享变量的原子性。

6.1、ABA问题

ABA问题指的是,在CAS操作期间,某个值从A变为B,然后再变回A,而CAS操作的结果可能会错误地判定为成功。这是因为CAS只比较当前值和预期值,不考虑中间是否发生过其他修改。解决ABA问题的方法是引入版本号,即将共享变量的值和版本号一起进行比较和交换。

import java.util.concurrent.atomic.AtomicStampedReference;

public class ABAExample {
    public static void main(String[] args) {
        AtomicStampedReference<Integer> atomicStampedRef =
            new AtomicStampedReference<>(1, 0);

        int initialStamp = atomicStampedRef.getStamp();
        int initialValue = atomicStampedRef.getReference();

        // 线程A执行操作,此时线程B修改了值,又恢复原值
        atomicStampedRef.compareAndSet(initialValue, 2, initialStamp, initialStamp + 1);
        
        // 线程C试图修改值,但由于版本号不匹配,CAS失败
        boolean success = atomicStampedRef.compareAndSet(initialValue, 3, initialStamp, initialStamp + 1);
    }
}

6.2、循环时间长

CAS操作是基于循环实现的,即在操作失败时会重试,直至操作成功为止。但当竞争激烈时,可能会导致循环时间较长,浪费CPU资源。解决这个问题的方法是在重试时加入一定的时间限制,避免无限循环。

import java.util.concurrent.atomic.AtomicInteger;

public class LongLoopExample {
    public static void main(String[] args) {
        AtomicInteger counter = new AtomicInteger(0);
        
        int expectedValue = 0;
        int newValue = 1;
        int maxRetries = 10;
        
        int retries = 0;
        while (retries < maxRetries) {
            if (counter.compareAndSet(expectedValue, newValue)) {
                // CAS成功,退出循环
                break;
            }
            retries++;
        }
    }
}

6.3、只能保证一个共享变量的原子性

CAS操作只能保证单个共享变量的原子性,不能解决多个共享变量之间的原子操作问题。这意味着如果需要同时修改多个共享变量,CAS可能无法保证这些操作的原子性。

import java.util.concurrent.atomic.AtomicInteger;

public class MultipleVariablesExample {
    public static void main(String[] args) {
        AtomicInteger value1 = new AtomicInteger(0);
        AtomicInteger value2 = new AtomicInteger(0);
        
        value1.incrementAndGet(); // CAS保证了value1的原子操作
        value2.incrementAndGet(); // 但不能保证value1和value2操作的原子性
    }
}

7、总结

虽然CAS存在这三大问题,但在很多情况下仍然是非常有用的,并且在并发编程中发挥着重要的作用。为了解决这些问题,有时需要结合其他技术,如引入版本号来解决ABA问题,或者使用锁来处理多个共享变量之间的复杂原子操作。

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

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

相关文章

【GeoDa实用技巧100例】017:制作平行坐标图

文章目录 一、平行坐标图介绍二、平行坐标图制作1. 加载数据2. 平行坐标图三、平行坐标图链接一、平行坐标图介绍 在一般坐标系中,3D是可视化探索不同点相关性的极限。为了探索高维空间(大于3维)的可视化问题,1885年法国数学家奥克南提出了平行坐标的解决方案。 平行坐标图…

跑步用什么耳机听歌才不会掉,精选的几款跑步掉的运动耳机

跑步是一项受欢迎的运动&#xff0c;而好的音乐能够让跑步变得更加有趣。但是&#xff0c;很多人在跑步过程中都会遇到耳机脱落的问题&#xff0c;这不仅会打扰运动的节奏&#xff0c;也会让人感到非常恼火。因此&#xff0c;选择一款适合跑步的耳机非常重要。在本文中&#xf…

220、仿真-基于51单片机ULN2003A步进电机正反转加减速LCD12864显示Proteus仿真设计(程序+Proteus仿真+配套资料等)

毕设帮助、开题指导、技术解答(有偿)见文未 目录 一、硬件设计 二、设计功能 三、Proteus仿真图 ​编辑 四、程序源码 资料包括&#xff1a; 需要完整的资料可以点击下面的名片加下我&#xff0c;找我要资源压缩包的百度网盘下载地址及提取码。 方案选择 单片机的选择 方…

开发环境搭建

Anaconda安装搭建Python环境 官网下载Anaconda anaconda官网安装Anaconda设置系统环境变量 按照实际安装路径新建填写红框环境变量 验证环境是否正常运行 WINR输入cmd conda --version python --version pip --version 显示版本信息即为正常 VSCODE Python ShiftCtrlP顶部…

信号

信号也是IPC中的一种&#xff0c;是和管道&#xff0c;消息队列&#xff0c;共享内存并列的概念。 本文参考&#xff1a; Linux中的信号_linux中信号_wolf鬼刀的博客-CSDN博客 Linux系统编程&#xff08;信号处理 sigacation函数和sigqueue函数 )_花落已飘的博客-CSDN博客 Linu…

【Alibaba中间件技术系列】「RocketMQ技术专题」让我们一起探索一下DefaultMQPushConsumer的实现原理及源码分析

RocketMQ开源是使用文件作为持久化工具&#xff0c;阿里内部未开源的性能会更高&#xff0c;使用oceanBase作为持久化工具。 在RocketMQ1.x和2.x使用zookeeper管理集群&#xff0c;3.x开始使用nameserver代替zk&#xff0c;更轻量级&#xff0c;此外RocketMQ的客户端拥有两种的…

【快应用】图片放大预览功能的实现

【关键词】 放大、image、background-position 【问题背景】 快应用中并没有直接的放大图片预览的功能&#xff0c;然而是可以利用现有的功能去实现图片的放大预览功能的。这样在快应用中浏览页面内容遇到图片时&#xff0c;遇到一些小图&#xff0c;觉得图片内容是不错的&am…

基于Three.js的WebXR渲染入门

1、Three.js 渲染管线快速概览 我不会花太多时间讨论 Three.JS 渲染管道的工作原理,因为它在互联网上有详细记录(例如,此链接)。 我将在下图中列出基础知识,以便更容易理解各个部分的去向。 2、WebXR 设备 API 入门 在我们深入了解 WebXR API 本身之前,您应该知道 WebX…

Linux如何改变文件的权限

Linux如何改变文件的权限 权限介绍权限更改关键字chmod通过数字修改通过字母修改 权限介绍 文件类型和文件权限由10个字符组成 文件的类型&#xff1a;- 表示文件&#xff0c; d 表示文件夹文件权限&#xff1a;r 表示读权限&#xff0c;w 表示写权限&#xff0c;x 表示执行权…

vue浏览器插件安装-各种问题

方法1&#xff1a;vue.js devtolls插件下载 https://blog.csdn.net/qq_55640378/article/details/131553642 下载地址&#xff1a; Tags vuejs/devtools GitHub npm install 或是 cnpm install 遇到的报错 设置淘宝镜像源&#xff08;推荐使用nrm&#xff0c;这一步是为…

219、仿真-基于51单片机L298直流电机开始停止正反转加减速Proteus仿真设计(程序+Proteus仿真+配套资料等)

毕设帮助、开题指导、技术解答(有偿)见文未 目录 一、硬件设计 二、设计功能 三、Proteus仿真图 四、程序源码 资料包括&#xff1a; 需要完整的资料可以点击下面的名片加下我&#xff0c;找我要资源压缩包的百度网盘下载地址及提取码。 方案选择 单片机的选择 方案一&a…

韦东山-电子量产工具项目:UI系统

代码结构 所有代码都已通过测试跑通&#xff0c;其中代码结构如下&#xff1a; 一、include文件夹 1.1 common.h #ifndef _COMMON_H #define _COMMON_Htypedef struct Region {int iLeftUpX; //区域左上方的坐标int iLeftUpY; //区域左下方的坐标int iWidth; //区域宽度…

英雄大战反派 Game Jam

围绕英雄与反派之间的冲突&#xff0c;制作惊心动魄的游戏。 所有游戏创作者和梦想家请注意&#xff01;准备释放您的创造力&#xff0c;进入一个充满冲突和传奇战役的世界。我们很高兴为您带来《英雄大战反派》终极 Game Jam&#xff0c;仅限使用 The Sandbox 的 Game Maker 进…

SVD奇异值分解相关技术

重要说明&#xff1a;本文从网上资料整理而来&#xff0c;仅记录博主学习相关知识点的过程&#xff0c;侵删。 一、参考资料 Eigen 矩阵的SVD分解 奇异值分解&#xff08;SVD&#xff09; 二、相关介绍 1. 单位阵 主对角线全为1的方阵&#xff0c;称为单位阵&#xff0c;…

unity 之 GetComponent 获取游戏对象上组件实例方法

GetComponent 简单介绍 GetComponent 是Unity引擎中用于获取游戏对象上组件实例的方法。它允许您从游戏对象中获取特定类型的组件&#xff0c;以便在脚本中进行操作和交互。 GetComponent< ComponentType >(): 这是一个泛型方法&#xff0c;用于从当前游戏对象上获取指定…

解锁数据中心高效监测精密空调,现学现用!

精密空调监控在当今科技发展中扮演着至关重要的角色。无论是数据中心、实验室还是医疗设施&#xff0c;都需要维持恒定的温度、湿度和空气质量&#xff0c;以确保设备的正常运行和数据的准确性。 精密空调监控不仅是现代高科技环境中的必备工具&#xff0c;也是保障设备稳定性、…

idea 本地版本控制 local history

idea 本地版本控制 local history 如何打开 1 自定义快捷键 settings->keymap->搜索框输入 show history -》Add Keyboard Shortcut -》设置为 CtrlAltL 2 右键文件-》local history -》show history 新建文件 版本1&#xff0c;creating class com.geekmice…这个是初…

同一个局域网主机中的一台主机连接另一台主机的虚拟机

星光下的赶路人star的个人主页 理想的路总是为有信心的人预备着 文章目录 1、描述问题2、解决前提3、解决办法4、实操4.1 虚拟机配置4.2 主机防火墙配置&#xff08;是你要连接虚拟机的所在的主机&#xff09;4.3 连接测试 1、描述问题 想要连接朋友主机的虚拟机&#xff0c;利…

光电效应波粒二象性

光电效应&#xff1a;光照射在金属表面时&#xff0c;瞬间&#xff08;t &#xff1c;10^-9s&#xff09;释放出电子的现象。 光电效应规律&#xff1a; 1、任何一种金属都有一个截止频率&#xff0c;低于该截止频率不能发生光电效应&#xff1b; 2、光电子最大初动能与入射光的…

“我30岁了,转行IT行业!还有没有出路?”

人到30&#xff0c;就容易产生中年危机。俗话说30而立&#xff0c;但其实很不容易&#xff0c;成家不易、立业也不易&#xff0c;尤其是现如今房价这么贵&#xff0c;物价那么高&#xff0c;各种压力随之而来。在职场中工作了几年&#xff0c;到近30岁时如果还是没有太大进展&a…