CompareAndSet(CAS)

转载:http://flychao88.iteye.com/blog/2269438

转载:https://blog.csdn.net/gooooooal/article/details/52061932?utm_source=blogxgwz4

一、CAS简介

CAS:Compare and Swap, 翻译成比较并交换。 

CAS指令在Intel CPU上称为CMPXCHG指令,它的作用是将指定内存地址的内容与所给的某个值相比,如果相等,则将其内容替换为指令中提供的新值,如果不相等,则更新失败。这一比较并交换的操作是原子的,不可以被中断。初一看,CAS也包含了读取、比较 (这也是种操作)和写入这三个操作,和之前的i++并没有太大区别,是的,的确在操作上没有区别,但CAS是通过硬件命令保证了原子性,而i++没有,且硬件级别的原子性比i++这样高级语言的软件级别的运行速度要快地多。虽然CAS也包含了多个操作,但其的运算是固定的(就是个比较),这样的锁定性能开销很小。

从内存领域来说这是乐观锁,因为它在对共享变量更新之前会先比较当前值是否与更新前的值一致,如果是,则更新,如果不是,则无限循环执行(称为自旋),直到当前值与更新前的值一致为止,才执行更新。

java.util.concurrent包中借助CAS实现了区别于synchronouse同步锁的一种乐观锁,使用这些类在多核CPU的机器上会有比较好的性能.

CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

今天我们主要是针对AtomicInteger的incrementAndGet做深入分析。

首先要说一下,AtomicInteger类compareAndSet通过原子操作实现了CAS操作,最底层基于汇编语言实现。

简单说一下原子操作的概念,“原子”代表最小的单位,所以原子操作可以看做最小的执行单位,该操作在执行完毕前不会被任何其他任务或事件打断。同一时刻,只有一个线程能用compareAndSet,内存锁定,同一时刻只有一个线程可以修改内存值,线程安全

二、JAVA实现部分

Java代码 

 收藏代码

  1. /** 
  2.      * Atomically increments by one the current value. 
  3.      * 
  4.      * @return the updated value 
  5.      */  
  6.     public final int incrementAndGet() {  
  7.         for (;;) {  
  8.             int current = get();  
  9.             int next = current + 1;  
  10.             if (compareAndSet(current, next))  
  11.                 return next;  
  12.         }  
  13.     }  

  循环的内容是
1.取得当前值
2.计算+1后的值
3.如果当前值没有被覆盖的话设置那个+1后的值
4.如果设置没成功, 再从1开始

在这个方法中可以看到compareAndSet这个方法,我们进入看一下。

Java代码 

 收藏代码

  1. /** 
  2.      * Atomically sets the value to the given updated value 
  3.      * if the current value {@code ==} the expected value. 
  4.      * 
  5.      * @param expect the expected value 
  6.      * @param update the new value 
  7.      * @return true if successful. False return indicates that 
  8.      * the actual value was not equal to the expected value. 
  9.      */  
  10.     public final boolean compareAndSet(int expect, int update) {  
  11.         return unsafe.compareAndSwapInt(this, valueOffset, expect, update);  
  12.     }  

 调用UnSafe这个类的compareAndSwapInt

  

Java代码 

 收藏代码

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

 JAVA程序也就跟踪到这里为止了,剩下的就是通过JNI调用C程序了,可是我奇怪的是为什么变量名都是var1,var2这样的命名呢?JAVA编程规范不是说不使用1,2等没有含义的字符命名吗?

三、JNI原生实现部分

在openJDK中找到找到unsafe.cpp这个文件,代码如下:

Java代码 

 收藏代码

  1. UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))  
  2.   UnsafeWrapper("Unsafe_CompareAndSwapInt");  
  3.   oop p = JNIHandles::resolve(obj);  
  4.   jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);  
  5.   return (jint)(Atomic::cmpxchg(x, addr, e)) == e;  
  6. UNSAFE_END  

 核心方法是compxchg,这个方法所属的类文件是在OS_CPU目录下面,由此可以看出这个类是和CPU操作有关,进入代码如下:

Java代码 

 收藏代码

  1. inline jint     Atomic::cmpxchg    (jint     exchange_value, volatile jint*     dest, jint     compare_value) {  
  2.   // alternative for InterlockedCompareExchange  
  3.   int mp = os::is_MP();  
  4.   __asm {  
  5.     mov edx, dest  
  6.     mov ecx, exchange_value  
  7.     mov eax, compare_value  
  8.     LOCK_IF_MP(mp)  
  9.     cmpxchg dword ptr [edx], ecx  
  10.   }  
  11. }  

 这个方法里面都是汇编指命,看到LOCK_IF_MP也有锁指令实现的原子操作,其实CAS也算是有锁操作,只不过是由CPU来触发,比synchronized性能好的多。

猜你喜欢

转载自blog.csdn.net/u014203449/article/details/83928439
Cas