深入理解java:2.3.1. 並發編程concurrent包 之Atomic原子操作(循環CAS)


java中,可能有一些場景,操作非常簡單,但是容易存在並發問題,比如i++,

此時,如果依賴鎖機制,可能帶來性能損耗等問題,

於是,如何更加簡單的實現原子性操作,就成為java中需要面對的一個問題。

 

在backport-util-concurrent沒有被引入java1.5並成為JUC之前,

這些原子類和原子操作方法,都是使用synchronized實現的。

不過JUC出現之后,這些原子操作 基於JNI提供了新的實現,

比如AtomicInteger,AtomicLong,AtomicBoolean,AtomicReference,AtomicIntegerArray/AtomicLongArray/AtomicReferenceArray;

這些操作中提供一些原子化操作,比如incrementAndGet(相當於i++),compareAndSet(安全賦值)等,直接讀源代碼也很容易懂。

 

以AtomicInteger為例,看看它是怎么做到的:

如果是讀取值,很簡單,將value聲明為volatile的,就可以保證在沒有鎖的情況下,數據是線程可見的:

1     private volatile int value;

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

 

那么,涉及到值變更的操作呢?以AtomicInteger實現:++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操作,如果成功就返回結果,否則重試直到成功為止。

而這里的comparAndSet(current,next),就是前面介紹CAS的時候所說的依賴JNI實現的樂觀鎖做法:

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

 

數組原子化

注意,Java中Atomic*Array,並不是對整個數組對象實現原子化(也沒有必要這樣做),而是對數組中的某個元素實現原子化。

例如,對於一個整型原子數組,其中的原子方法,都是對每個元素的:

復制代碼
1 public final int getAndDecrement(int i) { 2 while (true) { 3 int current = get(i); 4 int next = current - 1; 5 if (compareAndSet(i, current, next)) 6 return current; 7  } 8 } 
復制代碼

 

引用的原子化操作

引用的操作本身不就是原子的嗎?

一個對象的引用,從A切換到B,本身也不會出現 非原子操作啊?這種想法本身沒有什么問題,

但是考慮下嘛的場景:對象a,當前執行引用a1,

線程X期望將a的引用設置為a2,也就是a=a2,

線程Y期望將a的引用設置為a3,也就是a=a3。

 

X要求,a必須從a1變為a2,也就是說compareAndSet(expect=a1,setValue=a2);

Y要求,a必須從a1變為a3,也就是說compareAndSet(expect=a1,setValue=a3)。

如果嚴格遵循要求,應該出現X把a的引用設置為a2后,Y線程操作失敗的情況,也就是說:

X:a==a1--> a=a2;

Y:a!=a1 --> Exception;

如果沒有原子化,那么Y會直接將a賦值為a3,從而導致出現臟數據。

 

這就是原子引用AtomicReference存在的原因。

復制代碼
1      public final V getAndSet(V newValue) { 2 while (true) { 3 V x = get(); 4 if (compareAndSet(x, newValue)) 5 return x; 6  } 7 }
復制代碼

注意,AtomicReference要求引用也是volatile的。

 

Updater原子化

其它幾個Atomic類,都是對被volatile修飾的基本數據類型的自身數據進行原子化操作,

但是如果一個被volatile修飾的變量本身已經存在在類中,那要如何提供原子化操作呢?

 

比如,一個Person,其中有個屬性為age,private volatile int age,

如何對age提供原子化操作呢?

1 private AtomicIntegerFieldUpdater<Person> updater = AtomicIntegerFieldUpdater.newUpdater(Person.class, "age"); 2 updater.getAndIncrement(5);//加5歲 3 updater.compareAndSet(person, 30, 35)//如果一個人的年齡是30,設置為35。

 

Java中的Atomic包使用指南

參考: http://ifeve.com/java-atomic/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM