Atomic的介紹和使用(原子變數)
開始之前,我們來看一下上一篇文章中《CAS (全 ) && concurrent包的實現》中提到了concurrent包的實現圖。
下圖中的原子變數類就是Atomic類中的一部分。
也就是說,atomic類首先是一個樂觀鎖,然後底層實現也是根據CAS操作和Volatile關鍵字實現的。
什麼是執行緒安全?
**執行緒安全是指多執行緒訪問是時,無論執行緒的排程策略是什麼,程式能夠正確的執行。**導致執行緒不安全的一個原因是狀態不一致,如果執行緒A修改了某個共享變數(比如給id++),而執行緒B沒有及時知道,就會導致B在錯誤的狀態上執行,結果的正確性也就無法保證。原子變數為我們提供了一種保證單個狀態一致的簡單方式,一個執行緒修改了原子變數,另外的執行緒立即就能看到,這比通過鎖實現的方式效率要高;如果要同時保證多個變數狀態一致,就只能使用鎖了。
Atomic
在JDK1.5之後,JDK的(concurrent包)併發包裡提供了一些類來支援原子操作,如AtomicBoolean,AtomicInteger,AtomicLong都是用原子的方式來更新指定型別的值。
從多執行緒平行計算樂觀鎖 和 悲觀鎖 來講,JAVA中的synchronized 屬於悲觀鎖,即是在操作某資料的時候總是會認為多執行緒之間會相互干擾,屬於阻塞式的加鎖;Atomic系列則屬於樂觀鎖系列,即當操作某一段資料的時候,執行緒之間是不會相互影響,採用非阻塞的模式,直到更新資料的時候才會進行版本的判斷是否值已經進行了修改。
sun.misc.Unsafe
這個類包含了大量的對C程式碼的操作,包括了很多直接記憶體分配和原子操作的呼叫,都存在安全隱患,所以標記為unsafe。
AtomicInteger是一個標準的樂觀鎖實現,sun.misc.Unsafe是JDK提供的樂觀鎖的支援。
Atomic在JAVA中的家族如下:
a、基本類:AtomicInteger、AtomicLong、AtomicBoolean;
b、引用型別:AtomicReference、AtomicReference的ABA例項、AtomicStampedRerence、AtomicMarkableReference;
c、陣列型別:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
d、屬性原子修改器(Updater):AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater
Atomic的使用測試和講解 見:https://blog.csdn.net/xh16319/article/details/17056767
這裡我們不對每一個Atomic類進行詳細的測試和講解,我們只需要知道Atomic底層是通過CAS操作實現的資料的原子性,其中AtomicStampedReference類解決了ABA問題(ABA問題在《CAS (全 ) && concurrent包的實現》中也有講解)
我們在這裡只對 AtomicStampedReference 類和AtomicMarkableReference類的使用進行簡單描述,讀者理解其用法即可。
import java.util.concurrent.atomic.AtomicStampedReference;
public class AtomicStampedReferenceTest {
public final static AtomicStampedReference <String>ATOMIC_REFERENCE = new AtomicStampedReference<String>("abc" , 0);
public static void main(String []args) {
for(int i = 0 ; i < 100 ; i++) {
final int num = i;
final int stamp = ATOMIC_REFERENCE.getStamp();
new Thread() {
public void run() {
try {
Thread.sleep(Math.abs((int)(Math.random() * 100)));
} catch (InterruptedException e) {
e.printStackTrace();
}
if(ATOMIC_REFERENCE.compareAndSet("abc" , "abc2" , stamp , stamp + 1)) {
System.out.println("我是執行緒:" + num + ",我獲得了鎖進行了物件修改!");
}
}
}.start();
}
//下面這個執行緒是為了將資料改回原始值,以便之後的操作
new Thread() {
public void run() {
int stamp = ATOMIC_REFERENCE.getStamp();
while(!ATOMIC_REFERENCE.compareAndSet("abc2", "abc" , stamp , stamp + 1));
System.out.println("已經改回為原始值!");
}
}.start();
}
}
其中的compareAndSet函式的原始碼中就用到了之前文章中講解的CAS操作:
public final boolean compareAndSet(V expect, V update) {
return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}
類:AtomicMarkableReference和AtomicStampedReference功能差不多,有點區別的是:它描述更加簡單的是與否的關係,通常ABA問題只有兩種狀態,而AtomicStampedReference是多種狀態,那麼為什麼還要有AtomicMarkableReference呢,因為它在處理是與否上面更加具有可讀性,而AtomicStampedReference過於隨意定義狀態,並不便於閱讀大量的是和否的關係,它可以被認為是一個計數器或狀態列表等資訊,java提倡通過類名知道其意義,所以這個類的存在也是必要的,它的定義就是將資料變換為true|false如下:
public final static AtomicMarkableReference <String>ATOMIC_MARKABLE_REFERENCE = new AtomicMarkableReference<String>("abc" , false);
操作時使用:
ATOMIC_MARKABLE_REFERENCE.compareAndSet("abc", "abc2", false, true);
另外我們來簡單介紹一些Atomic中的擴充套件類:
java提供一個外部的Updater可以對物件的屬性本身的修改提供類似Atomic的操作,也就是它對這些普通的屬性的操作是併發下安全的,分別由:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceUpdater。
這樣操作後,系統會更加靈活,也就是可能那些類的屬性只是在某些情況下需要控制併發,很多時候不需要,但是他們的使用通常有以下幾個限制:
限制1:操作的目標不能是static型別,前面說到unsafe的已經可以猜測到它提取的是非static型別的屬性偏移量,如果是static型別在獲取時如果沒有使用對應的方法是會報錯的,而這個Updater並沒有使用對應的方法。
限制2:操作的目標不能是final型別的,因為final根本沒法修改。
限制3:必須是volatile型別的資料,也就是資料本身是讀一致的。
限制4:屬性必須對當前的Updater所在的區域是可見的,也就是private如果不是當前類肯定是不可見的,protected如果不存在父子關係也是不可見的,default如果不是在同一個package下也是不可見的。
**實現方式:**通過反射找到屬性,對屬性進行操作,但是並不是設定accessable,所以必須是可見的屬性才能操作。
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
public class AtomicIntegerFieldUpdaterTest {
static class A {
volatile int intValue = 100;
}
/**
* 可以直接訪問對應的變數,進行修改和處理
* 條件:要在可訪問的區域內,如果是private或挎包訪問default型別以及非父親類的protected均無法訪問到
* 其次訪問物件不能是static型別的變數(因為在計算屬性的偏移量的時候無法計算),也不能是final型別的變數(因為根本無法修改),必須是普通的成員變數
*
* 方法(說明上和AtomicInteger幾乎一致,唯一的區別是第一個引數需要傳入物件的引用)
* @see AtomicIntegerFieldUpdater#addAndGet(Object, int)
* @see AtomicIntegerFieldUpdater#compareAndSet(Object, int, int)
* @see AtomicIntegerFieldUpdater#decrementAndGet(Object)
* @see AtomicIntegerFieldUpdater#incrementAndGet(Object)
*
* @see AtomicIntegerFieldUpdater#getAndAdd(Object, int)
* @see AtomicIntegerFieldUpdater#getAndDecrement(Object)
* @see AtomicIntegerFieldUpdater#getAndIncrement(Object)
* @see AtomicIntegerFieldUpdater#getAndSet(Object, int)
*/
public final static AtomicIntegerFieldUpdater <A>ATOMIC_INTEGER_UPDATER = AtomicIntegerFieldUpdater.newUpdater(A.class, "intValue");
public static void main(String []args) {
final A a = new A();
for(int i = 0 ; i < 100 ; i++) {
final int num = i;
new Thread() {
public void run() {
if(ATOMIC_INTEGER_UPDATER.compareAndSet(a, 100, 120)) {
System.out.println("我是執行緒:" + num + " 我對對應的值做了修改!");
}
}
}.start();
}
}
}