Java多執行緒(二)之Atomic:原子變數與原子類
一、何謂Atomic?
Atomic一詞跟原子有點關係,後者曾被人認為是最小物質的單位。計算機中的Atomic是指不能分割成若干部分的意思。如果一段程式碼被認為是Atomic,則表示這段程式碼在執行過程中,是不能被中斷的。通常來說,原子指令由硬體提供,供軟體來實現原子方法(某個執行緒進入該方法後,就不會被中斷,直到其執行完成)
在x86 平臺上,CPU提供了在指令執行期間對匯流排加鎖的手段。CPU晶片上有一條引線#HLOCK pin,如果組合語言的程式中在一條指令前面加上字首"LOCK",經過彙編以後的機器程式碼就使CPU在執行這條指令的時候把#HLOCK pin的電位拉低,持續到這條指令結束時放開,從而把匯流排鎖住,這樣同一匯流排上別的CPU就暫時不能通過匯流排訪問記憶體了,保證了這條指令在多處理器環境中的原子性。
二、java.util.concurrent中的原子變數
無論是直接的還是間接的,幾乎 java.util.concurrent
包中的所有類都使用原子變數,而不使用同步。類似 ConcurrentLinkedQueue
的類也使用原子變數直接實現無等待演算法,而類似 ConcurrentHashMap
的類使用 ReentrantLock
在需要時進行鎖定。然後, ReentrantLock
使用原子變數來維護等待鎖定的執行緒佇列。
如果沒有 JDK 5.0 中的 JVM 改進,將無法構造這些類,這些改進暴露了(向類庫,而不是使用者類)介面來訪問硬體級的同步原語。然後,java.util.concurrent 中的原子變數類和其他類向使用者類公開這些功能
java.util.concurrent.atomic的原子類
這個包裡面提供了一組原子類。其基本的特性就是在多執行緒環境下,當有多個執行緒同時執行這些類的例項包含的方法時,具有排他性,即當某個執行緒進入方法,執行其中的指令時,不會被其他執行緒打斷,而別的執行緒就像自旋鎖一樣,一直等到該方法執行完成,才由JVM從等待佇列中選擇一個另一個執行緒進入,這只是一種邏輯上的理解。實際上是藉助硬體的相關指令來實現的,不會阻塞執行緒(或者說只是在硬體級別上阻塞了)。其中的類可以分成4組
- AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference
- AtomicIntegerArray,AtomicLongArray
- AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater
- AtomicMarkableReference,AtomicStampedReference,AtomicReferenceArray
其中AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference是類似的。
首先AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference內部api是類似的:舉個AtomicReference的例子
使用AtomicReference建立執行緒安全的堆疊
- public class LinkedStack<T> {
- private AtomicReference<Node<T>> stacks = new AtomicReference<Node<T>>();
- public T push(T e) {
- Node<T> oldNode, newNode;
- while (true) { //這裡的處理非常的特別,也是必須如此的。
- oldNode = stacks.get();
- newNode = new Node<T>(e, oldNode);
- if (stacks.compareAndSet(oldNode, newNode)) {
- return e;
- }
- }
- }
- public T pop() {
- Node<T> oldNode, newNode;
- while (true) {
- oldNode = stacks.get();
- newNode = oldNode.next;
- if (stacks.compareAndSet(oldNode, newNode)) {
- return oldNode.object;
- }
- }
- }
- private static final class Node<T> {
- private T object;
- private Node<T> next;
- private Node(T object, Node<T> next) {
- this.object = object;
- this.next = next;
- }
- }
- }
然後關注欄位的原子更新。
AtomicIntegerFieldUpdater<T>/AtomicLongFieldUpdater<T>/AtomicReferenceFieldUpdater<T,V>是基於反射的原子更新欄位的值。
相應的API也是非常簡單的,但是也是有一些約束的。
(1)欄位必須是volatile型別的!volatile到底是個什麼東西。請檢視 http://blog.csdn.net/a511596982/article/details/8201744
(2)欄位的描述型別(修飾符public/protected/default/private)是與呼叫者與操作物件欄位的關係一致。也就是說呼叫者能夠直接操作物件欄位,那麼就可以反射進行原子操作。但是對於父類的欄位,子類是不能直接操作的,儘管子類可以訪問父類的欄位。
(3)只能是例項變數,不能是類變數,也就是說不能加static關鍵字。
(4)只能是可修改變數,不能使final變數,因為final的語義就是不可修改。實際上final的語義和volatile是有衝突的,這兩個關鍵字不能同時存在。
(5)對於AtomicIntegerFieldUpdater和AtomicLongFieldUpdater只能修改int/long型別的欄位,不能修改其包裝型別(Integer/Long)。如果要修改包裝型別就需要使用AtomicReferenceFieldUpdater。
在下面的例子中描述了操作的方法。
- import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
- public class AtomicIntegerFieldUpdaterDemo {
- class DemoData{
- public volatile int value1 = 1;
- volatile int value2 = 2;
- protected volatile int value3 = 3;
- private volatile int value4 = 4;
- }
- AtomicIntegerFieldUpdater<DemoData> getUpdater(String fieldName) {
- return AtomicIntegerFieldUpdater.newUpdater(DemoData.class, fieldName);
- }
- void doit() {
- DemoData data = new DemoData();
- System.out.println("1 ==> "+getUpdater("value1").getAndSet(data, 10));
- System.out.println("3 ==> "+getUpdater("value2").incrementAndGet(data));
- System.out.println("2 ==> "+getUpdater("value3").decrementAndGet(data));
- System.out.println("true ==> "+getUpdater("value4").compareAndSet(data, 4, 5));
- }
- public static void main(String[] args) {
- AtomicIntegerFieldUpdaterDemo demo = new AtomicIntegerFieldUpdaterDemo();
- demo.doit();
- }
- }
在上面的例子中DemoData的欄位value3/value4對於AtomicIntegerFieldUpdaterDemo類是不可見的,因此通過反射是不能直接修改其值的。
AtomicMarkableReference類描述的一個<Object,Boolean>的對,可以原子的修改Object或者Boolean的值,這種資料結構在一些快取或者狀態描述中比較有用。這種結構在單個或者同時修改Object/Boolean的時候能夠有效的提高吞吐量。
AtomicStampedReference類維護帶有整數“標誌”的物件引用,可以用原子方式對其進行更新。對比AtomicMarkableReference類的<Object,Boolean>,AtomicStampedReference維護的是一種類似<Object,int>的資料結構,其實就是對物件(引用)的一個併發計數。但是與AtomicInteger不同的是,此資料結構可以攜帶一個物件引用(Object),並且能夠對此物件和計數同時進行原子操作。
在本文結尾會提到“ABA問題”,而AtomicMarkableReference/AtomicStampedReference在解決“ABA問題”上很有用。
三、Atomic類的作用
- 使得讓對單一資料的操作,實現了原子化
-
使用Atomic類構建複雜的,無需阻塞的程式碼
- 訪問對2個或2個以上的atomic變數(或者對單個atomic變數進行2次或2次以上的操作)通常認為是需要同步的,以達到讓這些操作能被作為一個原子單元。
-
無鎖定且無等待演算法
基於 CAS (compare and swap)的併發演算法稱為 無鎖定演算法,因為執行緒不必再等待鎖定(有時稱為互斥或關鍵部分,這取決於執行緒平臺的術語)。無論 CAS 操作成功還是失敗,在任何一種情況中,它都在可預知的時間內完成。如果 CAS 失敗,呼叫者可以重試 CAS 操作或採取其他適合的操作。
如果每個執行緒在其他執行緒任意延遲(或甚至失敗)時都將持續進行操作,就可以說該演算法是 無等待的。與此形成對比的是, 無鎖定演算法要求僅 某個執行緒總是執行操作。(無等待的另一種定義是保證每個執行緒在其有限的步驟中正確計算自己的操作,而不管其他執行緒的操作、計時、交叉或速度。這一限制可以是系統中執行緒數的函式;例如,如果有 10 個執行緒,每個執行緒都執行一次
CasCounter.increment()
操作,最壞的情況下,每個執行緒將必須重試最多九次,才能完成增加。)再過去的 15 年裡,人們已經對無等待且無鎖定演算法(也稱為 無阻塞演算法)進行了大量研究,許多人通用資料結構已經發現了無阻塞演算法。無阻塞演算法被廣泛用於作業系統和 JVM 級別,進行諸如執行緒和程式排程等任務。雖然它們的實現比較複雜,但相對於基於鎖定的備選演算法,它們有許多優點:可以避免優先順序倒置和死鎖等危險,競爭比較便宜,協調發生在更細的粒度級別,允許更高程度的並行機制等等。
常見的:
非阻塞的計數器Counter
非阻塞堆疊ConcurrentStack
非阻塞的連結串列ConcurrentLinkedQueue非阻塞演算法簡介:http://www.ibm.com/developerworks/cn/java/j-jtp04186/
ABA問題:
AtomicStampedReference
類支援這種方法。轉載: http://blog.csdn.net/vernonzheng/article/details/8206349
相關文章
- java基礎之執行緒 認識原子類Java執行緒
- Atomic 原子類詳解
- 【重學Java】多執行緒進階(執行緒池、原子性、併發工具類)Java執行緒
- 多執行緒系列(十六) -常用併發原子類詳解執行緒
- Java中atomic包中的原子操作類總結Java
- C#多執行緒系列(3):原子操作C#執行緒
- java多執行緒3:原子性,可見性,有序性Java執行緒
- 從concurrent下的Atomic原子類說起
- 原子操作atomic_t
- Atomic原子操作原理剖析
- Java併發程式設計之原子變數Java程式設計變數
- Java多執行緒(二):Thread類Java執行緒thread
- java多執行緒之Thread類Java執行緒thread
- C++多執行緒:atomicC++執行緒
- 多執行緒系列(二)之Thread類執行緒thread
- CAS 演算法與 Java 原子類演算法Java
- Go併發程式設計之原子操作sync/atomicGo程式設計
- 【java多執行緒】(二)執行緒停止Java執行緒
- 【Java多執行緒】輕鬆搞定Java多執行緒(二)Java執行緒
- Java多執行緒同步工具類之SemaphoreJava執行緒
- Java多執行緒同步工具類之CyclicBarrierJava執行緒
- Java多執行緒同步工具類之CountDownLatchJava執行緒CountDownLatch
- Java原子類操作原理剖析Java
- Java多執行緒之執行緒中止Java執行緒
- 原子操作與原子型別型別
- Java原子操作類,你知道多少?Java
- Java原子類實現原理分析Java
- Java中CAS演算法的集中體現:Atomic原子類庫,你瞭解嗎?Java演算法
- 深入理解Java多執行緒與併發框(第③篇)——Java記憶體模型與原子性、可見性、有序性Java執行緒記憶體模型
- Java多執行緒與併發之ThreadLocalJava執行緒thread
- Java鎖與非阻塞演算法的效能比較與分析+原子變數類的應用Java演算法變數
- JAVA重點類 多執行緒Java執行緒
- Java多執行緒Thread類使用Java執行緒thread
- Java高併發與多執行緒(二)-----執行緒的實現方式Java執行緒
- 多執行緒(2)-執行緒同步條件變數執行緒變數
- 【多執行緒總結(二)-執行緒安全與執行緒同步】執行緒
- Java多執行緒之CASJava執行緒
- Java多執行緒之FutureTaskJava執行緒
- java多執行緒之(synchronized)Java執行緒synchronized