当前位置: 首页 > news >正文

Java 中如何利用 CAS 实现原子操作?以AtomicInteger 为例

        在 Java 中,AtomicInteger是基于 CAS(Compare-and-Swap,比较并交换)机制实现原子操作的典型案例。它通过sun.misc.Unsafe类提供的 native 方法直接操作内存地址,确保多线程环境下对整数的修改具有原子性。以下是其实现原理的详细分析:

一、核心字段与 Unsafe 依赖

AtomicInteger的核心是通过Unsafe类操作一个volatile int变量,保证可见性和原子性:

  1. volatile int value:存储整数的实际值,volatile确保一个线程修改后,其他线程能立即看到最新值(可见性)。
  2. Unsafe:Java 提供的 “后门” 工具类,可直接操作内存地址,支持 CAS 等底层操作。
  3. valueOffsetvalue字段在对象内存中的偏移量Unsafe通过它定位到具体内存地址。

二、AtomicInteger 常用方法

1. public final int get() //获取当前的值

2. public final int getAndSet(int newValue) //获取当前的值,并设置新的值

3. public final int getAndIncrement() //获取当前的值,并自增

4. public final int getAndDecrement() //获取当前的值,并自减

5. public final int getAndAdd(int delta) //获取当前的值,并加上预期的值

6. boolean compareAndSet(int expect, int update) //如果输入的数值等于预期值,则以原子方式将该 值更新为输入值(update)

三、CAS 操作的核心实现:compareAndSet方法

AtomicInteger的所有原子操作(如自增、累加)最终都依赖compareAndSet方法,该方法通过UnsafecompareAndSwapInt实现 CAS:源码如下:

    public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}参数说明:
this:当前AtomicInteger对象。
valueOffset:value字段的内存偏移量。
expect:预期的当前值。
update:要更新的新值。底层逻辑:
原子性地检查value字段的当前值是否等于expect:
若等于,则将其更新为update,返回true。
若不等于,则不做修改,返回false。
整个过程由 CPU 指令保证原子性,无需加锁。

四、典型原子操作的实现:

AtomicInteger的其他方法(如自减、累加)均采用类似 “循环 + CAS” 的模式:

decrementAndGet(自减并返回新值):

addAndGet(累加并返回新值):

五、CAS 机制的优势与局限

优势:
  • 无锁开销:无需synchronized或锁机制,避免线程阻塞和上下文切换,性能更优(尤其低并发场景)。
  • 硬件级原子性:依赖 CPU 的 CAS 指令(如 x86 的cmpxchg),操作具有天然原子性。
局限:
  • ABA 问题:如前所述,值从 A→B→A 时,CAS 会误判为未修改(可通过AtomicStampedReference解决)。
  • 自旋开销:高并发下 CAS 失败率高,线程会频繁自旋重试,消耗 CPU 资源。
  • 只能保证单个变量的原子性:无法直接实现多个变量的复合原子操作。

总结:

AtomicInteger通过以下方式实现原子操作:

  1. volatile int value存储值,保证可见性。
  2. 依赖Unsafe类的compareAndSwapInt方法,直接操作内存地址实现 CAS。
  3. 所有原子操作(自增、自减等)通过 “循环 + CAS” 模式,确保操作失败时重试,直到成功。
http://www.dtcms.com/a/389270.html

相关文章:

  • Custom SRP - Point And Spot Shadows
  • 无障碍前端组件实践(上):基础交互组件与色彩无障碍
  • 矩阵的导数运算
  • 微算法科技(NASDAQ:MLGO)多注意力循环网络:MARN技术如何让机器理解语言、手势与语音的微妙交互
  • 混合架构(SpringCloud+Dubbo)的整合方案与适用场景(二)
  • centos的hadoop的允许hdfs命令覆盖linux系统目录文件或生成副本
  • 跨平台开发框架全景分析:Flutter、RN、KMM 与腾讯 Kuikly 谁更值得选择?
  • 燃料电池负载均衡测试:解锁高效供能密码
  • ip地址在哪里查看?怎样查询自己电脑ip?如何找到使用内网ip,判断看本地有无公网ip?内网ip怎么给外网访问?
  • 设计模式-模板方法模式详解
  • Red Hat 8.5.0-18 部署ceph文件系统
  • 将ceph文件存储挂载给k8s使用
  • ENVI系列教程(七)——自定义 RPC 文件图像正射校正
  • 「Java EE开发指南」如何用MyEclipse开发Java EE企业应用程序?(二)
  • Linux -- 传输层协议UDP
  • 使用Android Studio中自带的手机投屏功能
  • LeetCode:19.螺旋矩阵
  • Windows 命令行:在 cd 命令中使用绝对路径与相对路径
  • 图片修改尺寸
  • 《嵌入式硬件(十五):基于IMX6ULL的统一异步收发器(UART)的操作》
  • Python爬虫实战:研究Pandas,构建苏宁易购月饼销售数据采集与智能推荐系统
  • 导购app佣金模式的分布式计算架构:实时分账与财务对账
  • Linux Bash脚本自动创建keystore和生成公钥
  • 数据库管理员偏爱哪些MySQL数据库连接工具?
  • 大数据毕业设计选题推荐-基于大数据的农产品交易数据分析与可视化系统-Spark-Hadoop-Bigdata
  • MySQL C API 的“连接孵化器”-`mysql_init()`
  • oracle 数据库导入dmp文件
  • 第二部分:VTK核心类详解(第28章 vtkMatrix4x4矩阵类)
  • JDK、JRE、JVM 是什么?有什么关系?【Java】
  • Visual Studio 2022创建CPP项目