深入理解原子类的实现原理:CAS与解决方案

在并发编程中,线程安全是一个不可忽视的问题。Java 提供了一些原子类(如 AtomicIntegerAtomicLongAtomicReference 等),通过提供原子操作,解决了线程间对共享资源的并发访问问题。这些原子类的底层实现依赖于 CAS(Compare-And-Swap) 技术。本文将深入探讨 CAS 原理、原子类的实现以及常见问题的解决方案。

一、什么是 CAS(Compare-And-Swap)?

CAS,即比较并交换,是一种无锁的并发控制原语,它通过硬件支持来保证操作的原子性。CAS 的操作流程如下:

  1. 比较:检查某个变量当前的值是否等于预期的值。
  2. 替换:如果相等,则将该变量的值更新为新的值。
  3. 否则:如果不等,CAS 操作失败,通常会重试直到成功。

CAS 操作的工作原理:

CAS 可以保证操作的原子性,这意味着在多线程环境中,如果两个线程同时试图更新同一个变量,只有一个线程的 CAS 操作会成功,而另一个线程会重试。CAS 的优势在于它可以避免使用锁(如 synchronized),因此在高并发情况下,能够提高性能。

二、原子类的实现:通过 CAS 实现线程安全

Java 的 Atomic 类族(如 AtomicIntegerAtomicLongAtomicReference)都依赖于 CAS 来确保对共享变量的线程安全更新。这些类的底层通过 Unsafe 类来访问和操作内存,实现 CAS 操作。

1. Unsafe 类与 CAS

Unsafe 类是一个提供底层操作内存的工具类,Java 提供了 Unsafe 类来直接与内存进行交互。它是 Java 底层的一个重要工具,能够进行内存分配、访问字段、操作原子性等。

Atomic 类中,CAS 操作是通过 Unsafe 类中的一系列 compareAndSwap 方法实现的。例如,AtomicInteger 中的 getAndIncrement() 就是通过 compareAndSwapInt() 来实现的。

这些 compareAndSwapXxx 方法是 native 方法,意味着它们通过本地代码来实现,并由底层的硬件原语提供支持,从而实现原子操作。

2. CAS 操作实例

以下是一个简化的 CAS 操作实例:

public class AtomicIntegerExample {
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    private volatile int value;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset(AtomicIntegerExample.class.getDeclaredField("value"));
        } catch (Exception ex) {
            throw new Error(ex);
        }
    }

    public boolean compareAndSwap(int expected, int newValue) {
        return unsafe.compareAndSwapInt(this, valueOffset, expected, newValue);
    }
}

在上面的代码中,compareAndSwapInt() 方法通过 Unsafe 类的本地代码来执行 CAS 操作。compareAndSwapInt() 会在 value 值等于 expected 时将其更新为 newValue,并返回一个表示操作是否成功的布尔值。

三、CAS 面临的问题及解决方案

尽管 CAS 提供了高效的原子操作,但在实际应用中,它也会面临一些问题,尤其是在高并发环境下。下面是常见的 CAS 问题以及解决方案。

1. ABA 问题

ABA 问题是 CAS 操作的经典问题之一。假设一个线程在执行 CAS 操作时,某个变量的值为 A,CAS 操作成功更新该值为 B。但在另一个线程的干扰下,这个值从 A 变成了 B,然后又变回了 A。这时候 CAS 操作会认为值并没有改变,但实际上中间有数据变化,这就导致了错误。

解决方案:AtomicStampedReference

为了解决 ABA 问题,AtomicStampedReference 引入了一个版本号(stamp)。每次更新时,除了更新值本身外,还会更新版本号。当进行 CAS 操作时,不仅比较值,还比较版本号。如果版本号不同,CAS 操作就会失败,从而避免 ABA 问题。

AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);
int[] stamp = new int[1];
Integer currentValue = atomicStampedReference.get(stamp);

2. 自旋过长导致 CPU 资源浪费

在高并发环境中,如果多个线程不断竞争同一个变量的更新,会导致大量的自旋操作。CAS 操作虽然非常高效,但如果长时间无法成功,它会消耗大量的 CPU 资源。尤其是当多个线程持续竞争同一个原子变量时,只有一个线程能够成功,其它线程会一直自旋,浪费 CPU 时间。

解决方案:LongAdder

为了解决自旋问题,Java 8 引入了 LongAdder 类。LongAdder 通过将一个长整型值拆分为多个独立的变量(Cell 数组),使得多个线程可以同时更新不同的 Cell,从而减少线程竞争。当需要获取最终的结果时,LongAdder 会将所有的 Cell 值相加,得到最终值。

LongAdder longAdder = new LongAdder();
longAdder.increment();  // 会自增某个Cell
longAdder.sum();        // 获取总和

LongAdder 的设计思想是,尽量减少多线程之间对单一变量的竞争,避免了长时间自旋带来的性能损失。

3. 多个变量的 CAS 操作

CAS 是针对单一变量的操作。如果要对多个变量进行原子操作,CAS 无法直接处理。此时可以采用两种解决方案:

  1. 加锁:将多个变量的操作封装在一个同步代码块中,使用 synchronized 锁来确保原子性。
  2. 封装为对象:将多个变量封装成一个对象,在操作时直接对对象进行 CAS 操作。通过这种方式,可以在对象级别进行原子操作。

四、总结

CAS(Compare-And-Swap)是一种基于硬件原语的无锁并发控制技术,广泛应用于 Java 中的原子类(如 AtomicIntegerAtomicLong 等)。它通过原子操作确保多个线程对共享资源的安全访问。尽管 CAS 操作性能优秀,但也存在一些问题,如 ABA 问题和自旋过长导致的 CPU 浪费,Java 提供了 AtomicStampedReferenceLongAdder 等工具来解决这些问题。

掌握 CAS 操作和解决方案,可以帮助你更好地进行高并发编程,提升程序的性能和可维护性。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

#渗透测试#SRC漏洞挖掘#云技术基础02之容器与云

免责声明 本教程仅为合法的教学目的而准备&#xff0c;严禁用于任何形式的违法犯罪活动及其他商业行为&#xff0c;在使用本教程前&#xff0c;您应确保该行为符合当地的法律法规&#xff0c;继续阅读即表示您需自行承担所有操作的后果&#xff0c;如有异议&#xff0c;请立即停…

【Linux系统编程】第四十六弹---线程同步与生产消费模型深度解析

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、Linux线程同步 1.1、同步概念与竞态条件 1.2、条件变量 1.2.1、认识条件变量接口 1.2.2、举例子认识条件变量 1.2.3、…

力扣(LeetCode)283. 移动零(Java)

White graces&#xff1a;个人主页 &#x1f649;专栏推荐:Java入门知识&#x1f649; &#x1f439;今日诗词:雾失楼台&#xff0c;月迷津渡&#x1f439; ⛳️点赞 ☀️收藏⭐️关注&#x1f4ac;卑微小博主&#x1f64f; ⛳️点赞 ☀️收藏⭐️关注&#x1f4ac;卑微小博主…

如何在单片机引脚有限时拓展更多引脚

假设单片机有3个GPIO口可以使用&#xff0c;但是我现在要控制多余3个口的功能怎么办&#xff1f; 这个时候可以用到74LS138&#xff08;3 线&#xff0d;8线译码器&#xff09;&#xff1a; 这个时候我使用三位二进制位可以表示2^3 8个引脚的内容 这种方法经常用于选择数码屏…

go debug日记:protoc -I . helloworld.proto --go_out=plugins=grpc:.错误debug

使用protoc生成go的文件出现bug 运行命令 protoc -I . helloworld.proto --go_outpluginsgrpc:.如图所示 即&#xff0c;没有指定生成的go文件位置&#xff0c;需要在文件中添加 option go_package"path;name";其中 path 表示生成的go文件的存放地址&#xff0c;…

cesium渲染3DTiles模型和glb模型

cesium渲染3DTiles模型和glb模型 相关网站&#xff1a; 1.快速入门&#xff1a;https://cesium.com/learn/cesiumjs-learn/cesiumjs-quickstart/ 2.webpack配置&#xff1a;https://github.com/CesiumGS/cesium-webpack-example#cesium-webpack-example 3.说明文档&#xff…

灰狼优化算法

一、简介 1.1 灰狼优化算法-Grey Wolf Optimizer 通过模拟灰狼群体捕食行为&#xff0c;基于狼群群体协 作的机制来达到优化的目的。&#xff27;&#xff37;&#xff2f;算法具有结构简单、需 要调节的参数少、容易实现等特点&#xff0c;其中存在能够自适应调整 的收敛因子…

新日撸java三百行` 新手小白java学习记录 `Day1

新日撸java三百行新手小白java学习记录 Day1 模拟多线程回调机制 文章目录 新日撸java三百行 新手小白java学习记录 前言一 、模拟异步机制提出问题解决方案 前言 古人称长江为江&#xff0c;黄河为河。长江水清&#xff0c;黄河水浊&#xff0c;长江在流&#xff0c;黄河也在…

【Unity Bug 随记】unity version control 报 xx is not in a workspace.

可能原因是更改了仓库或者项目名称。 解决办法就是重置Unity Version Control&#xff0c;去Hub disconnect 然后重新connect cloud和UVC UVC可能连不上&#xff0c;直接进入项目就行&#xff0c;打开版本管理标签会让你重新连工作区&#xff0c;选择你的仓库和工作区 然后In…

Go语言入门教案

文章目录 一、教学目标二、教学重难点&#xff08;一&#xff09;重点&#xff08;二&#xff09;难点 三、教学方法四、教学过程&#xff08;一&#xff09;Go语言简介&#xff08;二&#xff09;环境搭建1. 下载和安装Go语言开发环境2. 配置Go语言环境变量3. 命令行查看Go语言…

[2024最新] java八股文实用版(附带原理)---java集合篇

介绍一下常见的list实现类&#xff1f; ArrayList 线程不安全&#xff0c;内部是通过数组实现的&#xff0c;继承了AbstractList&#xff0c;实现了List&#xff0c;适合随机查找和遍历&#xff0c;不适合插入和删除。排列有序&#xff0c;可重复&#xff0c;当容量不够的时候…

7天用Go从零实现分布式缓存GeeCache(学习)(3)

目录结构 ├── geecache │ ├── byteview.go │ ├── cache.go │ ├── consistenthash │ │ ├── consistenthash.go │ │ └── consistenthash_test.go │ ├── geecache.go │ ├── go.mod │ ├── http.go │ ├── lru │ …

OpenHarmony-1.启动流程

OpenHarmony启动流程 1.kernel的启动 流程图如下所示&#xff1a;   OpenHarmony(简称OH)的标准系统的底层系统是linux&#xff0c;所以调用如下代码&#xff1a; linux-5.10/init/main.c: noinline void __ref rest_init(void) {struct task_struct *tsk;int pid;rcu_sch…

HTB:Precious[WriteUP]

目录 连接至HTB服务器并启动靶机 使用nmap对靶机TCP端口进行开放扫描 使用curl访问靶机80端口 使用ffuf爆破一下子域 使用浏览器访问该域名 使用curl访问该域名响应头 使用exiftool工具查看该pdf信息 横向移动 USER_FLAG&#xff1a;adf5793a876a190f0c08b3b6247cec32…

jsmind 思维导图 + monaco-editor + vue3 + ts

Index.vue: <template><div class"m-jsmind-wrap"><div class"m-jsmind-header"><el-button type"primary" click"() > handleReset()">重置</el-button><el-button type"primary" cl…

在arm64架构下, Ubuntu 18.04.5 LTS 用命令安装和卸载qt4、qt5

问题&#xff1a;需要在 arm64下安装Qt&#xff0c;QT源码编译失败以后&#xff0c;选择在线安装&#xff01; 最后安装的版本是Qt5.9.5 和QtCreator 4.5.2 。 一、ubuntu安装qt4的命令(亲测有效)&#xff1a; sudo add-apt-repository ppa:rock-core/qt4 sudo apt updat…

AIGC学习笔记(5)——AI大模型开发工程师

文章目录 AI大模型开发工程师004 垂直领域的智能在线搜索平台1 智能在线搜索平台需求分析大模型不够“聪明”增强大模型的方式需求分析2 智能在线搜索平台方案设计方案设计技术选型大模型版本GLM-4大模型注册使用Google Cloud平台注册创建可编程的搜索引擎3 智能在线搜索平台代…

【React】状态管理之Redux

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 状态管理之Redux引言1. Redux 的核心概念1.1 单一数据源&#xff08;Single Sou…

Unity类银河战士恶魔城学习总结(P124 CharacterStats UI玩家的UI)

【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili 教程源地址&#xff1a;https://www.udemy.com/course/2d-rpg-alexdev/ 本章节实现了玩家属性栏&#xff0c;仓库&#xff0c;物品栏UI的制作 UI_StatSlot.cs 这个脚本是用来在Unity的UI上显示玩家属性&#xf…

蓝桥杯每日真题 - 第7天

题目&#xff1a;&#xff08;爬山&#xff09; 题目描述&#xff08;X届 C&C B组X题&#xff09; 解题思路&#xff1a; 前缀和构造&#xff1a;为了高效地计算子数组的和&#xff0c;我们可以先构造前缀和数组 a&#xff0c;其中 a[i] 表示从第 1 个元素到第 i 个元素的…