当前位置:主页 > 查看内容

CAS 原理深入剖析,深入内核源码的那种

发布时间:2021-04-27 00:00| 位朋友查看

简介:本文转载自微信公众号「KK架构师」,作者wangkai。转载本文请联系KK架构师公众号。 一、CAS 简介 CAS 的意思是 compare and swap,比较并交换。 CAS 的示意图如下: 比如一个很简单的操作,把变量 A = 2 加 1,结果为 3. 则先读取 A 的当前值 E 为 2,在内存……

 

本文转载自微信公众号「KK架构师」,作者wangkai。转载本文请联系KK架构公众号。 

一、CAS 简介

CAS 的意思是 compare and swap,比较并交换。

CAS 的示意图如下:

比如一个很简单的操作,把变量 A = 2 加 1,结果为 3.

则先读取 A 的当前值 E 为 2,在内存计算结果 V 为 3,比较之前读出来的 A 的当前值 2 和 最新值,如果最新值为 2 ,表示这个值没有被别人改过,则放心的把最终的值更新为 3.

有一种情况是,在你更新结果之前,其他有个线程在中途把 A 更新成了 5 ,又更新回了 2。但是在当前线程看起来,没有被改过。这就是 ABA 问题。

二、CAS 的实现

在 java 中,原子类都是用 cas 来实现的,我们可以看一看源码。

  1. public final int getAndIncrement() { 
  2.     return unsafe.getAndAddInt(this, valueOffset, 1); 

发现是调用了 unsafe 类的 getAndAddInt,继续看这个方法:

  1. public final int getAndAddInt(Object var1, long var2, int var4) { 
  2.     int var5; 
  3.     do { 
  4.         var5 = this.getIntVolatile(var1, var2); 
  5.     } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); 
  6.  
  7.     return var5; 

这里是一个 while 循环,直到比较成功,来看一下这个 compareAndSwapInt 方法

  1. public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5); 

发现这是一个 native 方法,意味着是 c 或者 c++ 写的虚拟机的实现。

在网上找到了 HotSpot 虚拟机的源码,找了一些资料发现了这个 compareAndSwapInt 的 c++ 代码:

在 unsafe.cpp 这个文件的 Unsafe_CompareAndSwapInt 这个方法里

发现最终调用的是 Atomic:: cmpxchg 方法,我们再找到 atomic_linux_x86.inline.hpp 这个文件

其中有一句 LOCK_IF_MP(%4) "cmpxchgl %1,(%3)"

LOCK_IF_MP 是一个宏,这个宏的定义是:

mp 的意思是 multi processor,意思是在多核 cpu 上,要锁一下这个指令。

到这里,结论是,最终调用了一条汇编指令:lock cmpxchg 指令,来实现底层 cas 的。

也就是 cpu 中有一条 cmpxchg 指令。

但是这条指令不是原子的,也就是拿出来和比较是两个操作,中间有可能被别人打断。

所以需要在这个过程加上 lock,意思是,我在对这个内存操作的过程中,不允许被别人打断。

可以简单理解为把内存总线锁住,别人不允许修改这块内存。

三、ABA 问题的解决

很简单,可以在数据上加上版本号即可,改了一次就新增一个版本号。

在 Java 中,可以使用 AtomicStampedReference 来解决这个问题

  1. import java.util.concurrent.atomic.AtomicInteger; 
  2. import java.util.concurrent.atomic.AtomicStampedReference; 
  3.  
  4. public class ABASingle { 
  5.  
  6.     public static void main(String[] args) { 
  7.         AtomicInteger atomicInt = new AtomicInteger(100); 
  8.         atomicInt.compareAndSet(100, 101); 
  9.         atomicInt.compareAndSet(101, 100); 
  10.         System.out.println("new value = " + atomicInt.get()); 
  11.         boolean result1 = atomicInt.compareAndSet(100, 101); 
  12.         System.out.println(result1); // result:true 
  13.  
  14.         AtomicInteger v1 = new AtomicInteger(100); 
  15.         AtomicInteger v2 = new AtomicInteger(101); 
  16.         AtomicStampedReference<AtomicInteger> stampedRef = new AtomicStampedReference<AtomicInteger>( 
  17.                 v1, 0); 
  18.  
  19.         int stamp = stampedRef.getStamp(); 
  20.         stampedRef.compareAndSet(v1, v2, stampedRef.getStamp(), 
  21.                 stampedRef.getStamp() + 1); 
  22.         stampedRef.compareAndSet(v2, v1, stampedRef.getStamp(), 
  23.                 stampedRef.getStamp() + 1); 
  24.         System.out.println("new value = " + stampedRef.getReference()); 
  25.         boolean result2 = stampedRef.compareAndSet(v1, v2, stamp, stamp + 1); 
  26.         System.out.println(result2); // result:false 
  27.     } 

本文转载自网络,原文链接:https://mp.weixin.qq.com/s/ahBCAUhsIe4hDFy--FBXTQ
本站部分内容转载于网络,版权归原作者所有,转载之目的在于传播更多优秀技术内容,如有侵权请联系QQ/微信:153890879删除,谢谢!

推荐图文


随机推荐