CAS

1、引言                                                                                                                       java

Java5以來,新增長的java.util.concurrent.atomic併發包中的一些原子類,是對Java部分數據類型的原子封裝,在原有數據類型的基礎上,提供了原子性的操做方法,保證了線程安全。其就是創建在CAS之上的。算法

下面以AtomicInteger爲例,來看一下是如何實現原子性操做的? 編程

 1 public final int incrementAndGet() {
 2 
 3 for (;;) {
 4 
 5 int current = get();
 6 
 7 int next = current + 1;
 8 
 9 if (compareAndSet(current, next))
10 
11 return next;
12 
13 }
14 
15 }
16 
17 public final int decrementAndGet() {
18 
19 for (;;) {
20 
21 int current = get();
22 
23 int next = current - 1;
24 
25 if (compareAndSet(current, next))
26 
27 return next;
28 
29 }
30 
31 }

   

以這兩個方法爲例,incrementAndGet方法至關於原子性的++i,decrementAndGet方法至關於原子性的--i(咱們知道++i或--i不是一個原子性的操做),這兩個方法中都沒有使用阻塞式的方式來保證原子性(如Synchronized),那它們是如何保證原子性的呢,下面引出CAS。 安全

   

2、Compare And Swap                                                                                                併發

       CAS 指的是現代 CPU 普遍支持的一種對內存中的共享數據進行操做的一種特殊指令。這個指令會對內存中的共享數據作原子的讀寫操做。簡單介紹一下這個指令的操做過程:首先,CPU 會將內存中將要被更改的數據與指望的值作比較。而後,當這兩個值相等時,CPU 纔會將內存中的數值替換爲新的值。不然便不作操做。最後,CPU 會將舊的數值返回。這一系列的操做是原子的。它們雖然看似複雜,但倒是 Java 5 併發機制優於原有鎖機制的根本。簡單來講,CAS 的含義是"我認爲原有的值應該是什麼,若是是,則將原有的值更新爲新值,不然不作修改,並告訴我原來的值是多少"。(這段描述引自《Java併發編程實踐》) this

      咱們能夠認爲,CAS的操做包括3個操做數:內存位置V,預期原值A,新值B。atom

當且僅當預期值A和內存值V相同時,將內存值V修改成B,不然返回V。所以能夠理解CAS的主要就是兩個步驟:衝突檢測和數據更新。spa

這是一種樂觀鎖的思路,它相信在它修改以前,沒有其它線程去修改它;而Synchronized是一種悲觀鎖,它認爲在它修改以前,必定會有其它線程去修改它,悲觀鎖效率很低。.net

volatile變量線程

與鎖相比,volatile變量是一個更輕量級的同步機制,由於在使用這些變量時不會發生上下文切換和線程調度等操做,可是volatile不能解決原子性問題,所以同步仍是須要鎖機制來解決。

//首先聲明瞭一個volatile變量value,咱們知道volatile保證了變量的內存可見性,也就是全部工做線程中同一時刻均可以獲得一致的值。

private volatile int value;

public final int get() {

return value;

}

 

下面來看一下AtomicInteger是如何利用CAS實現原子性操做的。   

3、Compare And Set                                                                                                     

 1 // setup to use Unsafe.compareAndSwapInt for updates
 2 
 3 private static final Unsafe unsafe = Unsafe.getUnsafe();
 4 
 5 private static final long valueOffset;// 注意是靜態的
 6 
 7    
 8 
 9 static {
10 
11 try {
12 
13 valueOffset = unsafe.objectFieldOffset
14 
15 (AtomicInteger.class.getDeclaredField("value"));// 反射出value屬性,獲取其在內存中的位置
16 
17 } catch (Exception ex) { throw new Error(ex); }
18 
19 }
20 
21    
22 
23 public final boolean compareAndSet(int expect, int update) {
24 
25 return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
26 
27 }

 

比較並設置,這裏利用Unsafe類的JNI方法實現,使用CAS指令,能夠保證讀-改-寫是一個原子操做。compareAndSwapInt有4個參數,this - 當前AtomicInteger對象,Offset - value屬性在內存中的位置(須要強調的是否是value值在內存中的位置),expect - 預期值,update - 新值,根據上面的CAS操做過程,當內存中的value值等於expect值時,則將內存中的value值更新爲update值,並返回true,不然返回false。在這裏咱們有必要對Unsafe有一個簡單點的認識,從名字上來看,不安全,確實,這個類是用於執行低級別的、不安全操做的方法集合,這個類中的方法大部分是對內存的直接操做,因此不安全,但當咱們使用反射、併發包時,都間接的用到了Unsafe。

   

循環設置

如今在來看開篇提到的兩個方法,咱們拿incrementAndGet來分析一下其實現過程。

 1 public final int incrementAndGet() {
 2 
 3 for (;;) {// 這樣優於while(true)
 4 
 5 int current = get();// 獲取當前值
 6 
 7 int next = current + 1;// 設置更新值
 8 
 9 if (compareAndSet(current, next))
10 
11 return next;
12 
13 }
14 
15 }

 

循環內,獲取當前值並設置更新值,調用compareAndSet進行CAS操做,若是成功就返回更新至,不然重試到成功爲止。這裏可能存在一個隱患,那就是循環時間過長,老是在當前線程compareAndSet時,有另外一個線程設置了value,這個固然是屬於小几率時間,目前Java貌似還不能處理這種狀況。

   

缺點

雖然使用CAS能夠實現非阻塞式的原子性操做,可是會產生ABA問題,下篇文章闡述關於ABA問題。

   

4、小結                                                                                                                              

現代的CPU提供了特殊的指令,能夠自動更新共享數據,並且可以檢測到其餘線程的干擾,而 compareAndSet() 就用這些代替了鎖定。

在沒有鎖的機制下須要藉助volatile原語,保證線程間的數據是可見的(共享的)。這樣才獲取變量的值的時候才能直接讀取。

樂觀鎖能夠認爲是一種思想,CAS(Compare and Swap)是樂觀鎖的一種實現。

Compare And Set總體的過程就是這樣子的,利用CPU的CAS指令,同時藉助JNI來完成Java的非阻塞算法。其它原子操做都是利用相似的特性完成的。

   

轉載 http://blog.csdn.net/ghsau/article/details/38471987

相關文章
相關標籤/搜索