码迷,mamicode.com
首页 > 其他好文 > 详细

CAS指令

时间:2021-03-18 14:36:39      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:wapi   cpu   asp   pen   加载   版本   api   var   交换   

 
CAS是CPU的一条指令,其具有原子性,原子性是由CPU硬件层面保证的。
 
CAS原语有三个操作数——内存位置(V)、预期原值(A)、新值(B)。若内存位置与预期原值匹配则处理器将该位置更新为新值。否则不做操作。无论何种情况都会在CAS指令之前返回该位置值。这个过程是原子性的。
 
底层硬件通过将 CAS 里的多个操作在硬件层面语义实现上,通过一条处理器指令保证了原子性操作。这些指令如下所示:
(1)测试并设置(Tetst-and-Set)
(2)获取并增加(Fetch-and-Increment)
(3)交换(Swap)
(4)比较并交换(Compare-and-Swap)
(5)加载链接/条件存储(Load-Linked/Store-Conditional)
前面三条大部分处理器已经实现,后面的两条是现代处理器当中新增加的。而且根据不同的体系结构,指令存在着明显差异。
在IA64,x86 指令集中有 cmpxchg 指令完成 CAS 功能。
 
sun.misc.Unsafe 中 CAS 的核心方法:
这里的实现和CAS原语基本一致,唯一的区别就是返回类型不一样,CAS指令返回内存地址最新值,这里成功写入返回true,失败返回false
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);

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

public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
这三个方法可以对应去查看 openjdk 的 hotspot 源码:
源码位置:hotspot/src/share/vm/prims/unsafe.cpp
#define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f)

{CC"compareAndSwapObject", CC"("OBJ"J"OBJ""OBJ")Z",  FN_PTR(Unsafe_CompareAndSwapObject)},

{CC"compareAndSwapInt",  CC"("OBJ"J""I""I"")Z",      FN_PTR(Unsafe_CompareAndSwapInt)},

{CC"compareAndSwapLong", CC"("OBJ"J""J""J"")Z",      FN_PTR(Unsafe_CompareAndSwapLong)},
三个方法,最终在 hotspot 源码实现中都会调用统一的 cmpxchg 函数,可以在 hotspot 源码中找到核心代码。
源码地址:hotspot/src/share/vm/runtime/Atomic.cpp
 
cmpxchg 函数源码:可以看到调用了cmpxchg汇编指令执行CAS操作
 1 jbyte Atomic::cmpxchg(jbyte exchange_value, volatile jbyte*dest, jbyte compare_value) {
 2          assert (sizeof(jbyte) == 1,"assumption.");
 3          uintptr_t dest_addr = (uintptr_t) dest;
 4          uintptr_t offset = dest_addr % sizeof(jint);
 5          volatile jint*dest_int = ( volatile jint*)(dest_addr - offset);
 6          // 对象当前值
 7          jint cur = *dest_int;
 8          // 当前值cur的地址
 9          jbyte * cur_as_bytes = (jbyte *) ( & cur);
10          // new_val地址
11          jint new_val = cur;
12          jbyte * new_val_as_bytes = (jbyte *) ( & new_val);
13           // new_val存exchange_value,后面修改则直接从new_val中取值
14          new_val_as_bytes[offset] = exchange_value;
15          // 比较当前值与期望值,如果相同则更新,不同则直接返回
16          while (cur_as_bytes[offset] == compare_value) {
17           // 调用汇编指令cmpxchg执行CAS操作,期望值为cur,更新值为new_val
18              jint res = cmpxchg(new_val, dest_int, cur);
19              if (res == cur) break;
20              cur = res;
21              new_val = cur;
22              new_val_as_bytes[offset] = exchange_value;
23          }
24          // 返回当前值
25          return cur_as_bytes[offset];
26 }
 
ABA问题
CAS操作是判断当前内存中的值与期望值是否相等,相等才更新。但如果第一个线程执行CAS操作,其期望值是对象A,其它线程在这之前吧内存中的值改为了B,又改为了A。这时会判断相等,但实际A的属性可能已经改变,判断条件已经发生了变化,这就产生了线程安全的问题。
解决方法:
  添加版本号,每次更新内存操作时版本号加1,比较原值的同时比较版本号。
AtomicStampReference
AtomicStampReference在cas的基础上增加了一个标记stamp,使用pair将原数据包裹起来,基于pair进行底层cas操作。
 1 // 类的定义:
 2 public class AtomicStampedReference<V>
 3 // 构造函数,将对象和标记值传入
 4 public AtomicStampedReference(V initialRef, int initialStamp) {
 5     pair = Pair.of(initialRef, initialStamp);
 6 }
 7 // 参数代表的含义分别是 期望值,写入的新值,期望标记,新标记值
 8 public boolean compareAndSet(V   expectedReference,
 9                              V   newReference,
10                              int expectedStamp,
11                              int newStamp) {
12     Pair<V> current = pair;
13     // 比较原对象的同时比较版本号是否也相同,如果都相同则进行pair的cas操作
14     return
15         expectedReference == current.reference &&
16         expectedStamp == current.stamp &&
17         ((newReference == current.reference &&
18           newStamp == current.stamp) ||
19          casPair(current, Pair.of(newReference, newStamp)));
20 }
21 // 如果在这之前已经有线程对pair进行更新,则会执行失败
22 private boolean casPair(Pair<V> cmp, Pair<V> val) {
23     return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
24 }
25 public V getRerference();
26 public int getStamp();
27 public void set(V newReference,int newStamp);

 

 

CAS指令

标签:wapi   cpu   asp   pen   加载   版本   api   var   交换   

原文地址:https://www.cnblogs.com/ningbing/p/14553400.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!