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包的實現》中也有講解)
AtomicInteger
AtomicInteger 類主要利用 CAS (compare and swap) + volatile 和 native 方法來保證原子操作,從而避免 synchronized 的高開銷,執行效率大為提升。
CAS的原理是拿期望的值和原本的一個值作比較,如果相同則更新成新的值。UnSafe 類的 objectFieldOffset() 方法是一個本地方法,這個方法是用來拿到“原來的值”的內存地址。另外 value 是一個volatile變量,在內存中可見,因此 JVM 可以保證任何時刻任何線程總能拿到該變量的最新值。
部分源碼:
常用方法
AtomicInteger和AtomicLong、AtomicBoolean三個類提供的方法幾乎相同。
AtomicInteger 類常用方法:
示例:
import java.util.concurrent.atomic.AtomicInteger;public class AtomicIntegerTest {public static void main(String[] args) {// TODO Auto-generated method stubint temvalue = 0;AtomicInteger i = new AtomicInteger(0);temvalue = i.getAndSet(3);System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:0; i:3temvalue = i.getAndIncrement();System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:3; i:4temvalue = i.getAndAdd(5);System.out.println("temvalue:" + temvalue + "; i:" + i);//temvalue:4; i:9}}使用Synchronized和AtomicInteger的示例:
不使用AtomicInteger而是使用基本類型:
class Test {private volatile int count = 0;//若要線程安全執行執行count++,需要加鎖public synchronized void increment() {count++; }public int getCount() {return count;} }使用AtomicInteger:
class Test2 {private AtomicInteger count = new AtomicInteger();public void increment() {count.incrementAndGet();}//使用AtomicInteger之后,不需要加鎖,也可以實現線程安全。public int getCount() {return count.get();} }AtomicStampedReference 類和AtomicMarkableReference類
我們在這里只對 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功能差不多,有點區別的是:AtomicMarkableReference描述更加簡單的是與否的關系,通常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(); } } }總結
以上是生活随笔為你收集整理的Atomic的介绍和使用(原子变量)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Mac终端链接服务器记住密码
- 下一篇: 【逗老师的无线电】宝峰神机刷OpenGD