CAS 解析

举例说明

AtomicInteger 原子整型类为例,一起来分析下 CAS 底层实现机制。
  atomicData.incrementAndGet()
源码如下所示:

// 自增方法 public final int getAndIncrement() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return current; } } // 额外提供的compareAndSet方法 public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }

  我们看到了 AtomicInteger 内部方法都是基于 Unsafe 类实现的,Unsafe 类是个跟底层硬件CPU指令通讯的复制工具类,保证原子性。

解析 CAS

unsafe.compareAndSwapInt(this, valueOffset, expect, update)
  所谓的 CAS,就是 Compare And Swap,对比之后交换数据。从oldValue替换为newValue。
  上面的方法,有几个重要的参数:

  1. this,Unsafe 对象本身,需要通过这个类来获取 value 的内存偏移地址。
  2. valueOffset,value 变量的内存偏移地址。
  3. expect,期望更新的值。
  4. update,要更新的最新值。

  如果原子变量中的 value 值(AtomicInteger中的变量)等于 expect,则使用 update 值更新 value 值并返回 true,否则返回 false。
  涉及到CAS 必然存在 value 这样的属性。

valueOffset 参数

// Unsafe实例 private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { // 获得value在AtomicInteger中的偏移量 valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } // 实际变量的值 private volatile int value;

valueOffset这个静态变量又是用来做什么的呢?
  这时候我们就得看看它的赋值来源了。根据 API 文档,UnsafeobjectFieldOffset方法可以获取成员属性在内存中的地址相对于对象内存地址的偏移量。说得简单点就是找到这个成员变量在内存中的地址,便于后续通过内存地址直接进行操作。

基于我的理解

  valueOffset定位的是内存中对象的属性的地址(创建对象时内存分配的,不变),地址用于存储实际值的地址(比如:4 或5 的存储地址)。
  对象初始化时,value 为 空,而valueOffeSet是有值的。
  CAS操作,就是将属性地址中的实际值与export进行对比。如果相同,将属性地址指向 update 地址。从而做到原子操作。
  操作方式是通过UnSafe实现的。
  volatile对于底层应该使用了 缓存锁定(CPU如何实现原子操作(2点)),以到达只有一个线程读的效果。
  由于value变量由volatile修饰,属性地址指向发生变化,导致value的值进行变更,从而保证多线程下的可见性。

java中CAS操作实现

java中是无法直接使用UnsafeCAS方法的。
Unsafe类中存在Unsafe.getUnsafe(),但创建后执行报错。如果想使用只能使用反射的方式。

public class Test { public volatile int num = 0; // 此处如果使用Integer,其结果也是 false。 Integer是封装类 public static void main(String[] args) throws Exception { // theUnsafe 这个对象是 Unsafe的一个属性,但可以认为就是 unsafe // 能且只能通过反射的方式获取到 unsafe对象 Field field = Unsafe.class.getDeclaredField("theUnsafe"); field.setAccessible(true); Unsafe unsafe = (Unsafe)field.get(null); Test test = new Test(); long numOffset = unsafe.objectFieldOffset(Test.class.getDeclaredField("num")); // offset 属性在对象中的内存偏移量,需要指定好属性在堆内存中的内存开始位置(对象初始化时就可以知道了) // numOffset 就是是 num属性在对象中的位置 boolean result = unsafe.compareAndSwapInt(test, numOffset, 0, 1); System.out.println("测试cas结果:" + result); } }

image

CAS 存在的难问题

  • ABA情况
  • 无限循环问题
  • cas只能针对一个属性的值,不能修改一段代码的操作。

__EOF__

本文作者之士咖啡
本文链接https://www.cnblogs.com/zz-1q/p/17755305.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   之士咖啡  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示