转自:

 

  http://blog.csdn.net/hsuxu/article/details/9467651

 

 

CAS

  CAS: Compare and Swap

  java.util.concurrent包中借助CAS实现了区别于synchronouse同步锁的一种乐观锁。

  CAS 指的是现代 CPU 广泛支持的一种对内存中的共享数据进行操作的一种特殊指令。这个指令会对内存中的共享数据做原子的读写操作。简 单介绍一下这个指令的操作过程:首先,CPU 会将内存中将要被更改的数据与期望的值做比较。然后,当这两个值相等时,CPU 才会将内存中的数值替换为新的值。否则便不做操作。最后,CPU 会将旧的数值返回。这一系列的操作是原子的。它们虽然看似复杂,但却是 Java 5 并发机制优于原有锁机制的根本。简单来说,CAS 的含义是“我认为原有的值应该是什么,如果是,则将原有的值更新为新值,否则不做修改,并告诉我原来的值是多少”。(这段描述引自《Java并发编程实践》)
       简单的来说,CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则返回V。这是一种乐观锁的思路,它相信在它修改之前,没有其它线程去修改它;而Synchronized是一种悲观锁,它认为在它修改之前,一定会有其它线程去修改它,悲观锁效率很低

 

CAS应用

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

 

非阻塞算法 (nonblocking algorithms)

  一个线程的失败或者挂起不应该影响其他线程的失败或挂起的算法。

 

现代的CPU提供了特殊的指令,可以自动更新共享数据,而且能够检测到其他线程的干扰,而 compareAndSet() 就用这些代替了锁定。

拿出AtomicInteger来研究在没有锁的情况下是如何做到数据正确性的。

1 private volatile int value;

首先毫无以为,在没有锁的机制下可能需要借助volatile原语,保证线程间的数据是可见的(共享的)。

这样才获取变量的值的时候才能直接读取。

1 public final int get() {
2         return value;
3 }

 

然后来看看++i是怎么做到的。

1 public final int incrementAndGet() {
2     for (;;) {
3         int current = get();
4         int next = current + 1;
5         if (compareAndSet(current, next))
6             return next;
7     }
8 }

 

在这里采用了CAS操作,每次从内存中读取数据然后将此数据和+1后的结果进行CAS操作,如果成功就返回结果,否则重试直到成功为止。

而compareAndSet利用JNI来完成CPU指令的操作。

1 public final boolean compareAndSet(int expect, int update) {   
2     return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
3 }

整体的过程就是这样子的,利用CPU的CAS指令,同时借助JNI来完成Java的非阻塞算法。其它原子操作都是利用类似的特性完成的。

 

其中 unsafe.compareAndSwapInt(this, valueOffset, expect, update); 类似:

1 if (this == expect) {
2   this = update;
3   return true;
4 } else {
5   return false;
6 }

那么问题就来了,成功过程中需要2个步骤:比较this == expect,替换this = update,compareAndSwapInt如何这两个步骤的原子性呢? 参考CAS的原理。

 

CAS原理

  CAS通过调用JNI的代码实现的。JNI:Java Native Interface为JAVA本地调用,允许java调用其他语言。

  而compareAndSwapInt就是借助C来调用CPU底层指令实现的。

下面从分析比较常用的CPU(intel x86)来解释CAS的实现原理。

下面是sun.misc.Unsafe类的compareAndSwapInt()方法的源代码:

public final native boolean compareAndSwapInt(Object o, long offset,
                                              int expected,
                                              int x);

 

可以看到这是个本地方法调用。这个本地方法在openjdk中依次调用的c++代码为:unsafe.cpp,atomic.cpp和atomicwindowsx86.inline.hpp。这个本地方法的最终实现在openjdk的如下位置:openjdk-7-fcs-src-b147-27jun2011\openjdk\hotspot\src\oscpu\windowsx86\vm\ atomicwindowsx86.inline.hpp(对应于windows操作系统,X86处理器)。下面是对应于intel x86处理器的源代码的片段:

 

 1 // Adding a lock prefix to an instruction on MP machine
 2 // VC++ doesn't like the lock prefix to be on a single line
 3 // so we can't insert a label after the lock prefix.
 4 // By emitting a lock prefix, we can define a label after it.
 5 #define LOCK_IF_MP(mp) __asm cmp mp, 0  \
 6                        __asm je L0      \
 7                        __asm _emit 0xF0 \
 8                        __asm L0:
 9 
10 inline jint     Atomic::cmpxchg    (jint     exchange_value, volatile jint*     dest, jint     compare_value) {
11   // alternative for InterlockedCompareExchange
12   int mp = os::is_MP();
13   __asm {
14     mov edx, dest
15     mov ecx, exchange_value
16     mov eax, compare_value
17     LOCK_IF_MP(mp)
18     cmpxchg dword ptr [edx], ecx
19   }
20 }
View Code

相关文章:

  • 2022-01-05
  • 2022-12-23
  • 2021-08-12
  • 2022-01-12
猜你喜欢
  • 2021-12-05
  • 2022-01-09
  • 2021-05-05
  • 2021-07-04
  • 2021-05-29
相关资源
相似解决方案