【Java並發編程實戰】----- AQS(一):簡介


在前面博客中,LZ講到了ReentrantLock、ReentrantReadWriteLock、Semaphore、CountDownLatch,他們都有各自獲取鎖的方法,同時相對於Java的內置鎖,他們具有明顯的優勢:花最小的空間開銷創建鎖、最少的時間開銷獲得鎖、使用更加方便靈活。

參考Java的內置鎖,對於JUC同步器而言,他應該具備兩個最基本的功能:獲取鎖,釋放鎖。其中獲取鎖應該是先判斷當前狀態是否可以獲取,如果不可以獲取則處於阻塞狀態,釋放應該是釋放后修改狀態,讓其他線程能夠得到該鎖(喚醒其他線程),如下:

lock:
        while(state){                //
            getLock();            //獲取鎖
            return currentThread;        //返回當前線程
        }
        
        release:
        updateState();                //修改狀態    
        notify other thread;            //喚醒其他線程

我們知道,在JUC中,各個同步器獲取鎖和釋放鎖的方法都不相同,比如:lock.lock()、Semaphore.acquire()、 CountDownLatch.await()等等,假如他們都各自實現各自的方法,那么這個JUC框架頂多只能算一個中下等的框架設計了。這是AQS騰空出世,AQS作為一個核心的處理框架,他提供了大量的同步操作,同時用戶還可以在此類的基礎上進行自定義,實現自己的同步器。其主要框架如下:

2015112500003

從上圖可以看出AQS是JUC同步器的基石。下面就AQS涉及的技術原理簡單闡述下,以后會對其做詳細的說明。

1、狀態位state

AQS用的是一個32位的整型來表示同步狀態的,它是用volatile修飾的:

private volatile int state;

在互斥鎖中它表示着線程是否已經獲取了鎖,0未獲取,1已經獲取了,大於1表示重入數。同時AQS提供了getState()、setState()、compareAndSetState()方法來獲取和修改該值:

protected final int getState() {
            return state;
        }
        
        protected final void setState(int newState) {
            state = newState;
        }
        
        protected final boolean compareAndSetState(int expect, int update) {
            return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
        }

這些方法需要java.util.concurrent.atomic包的支持,采用CAS操作,保證其原則性和可見性。

2、CLH同步隊列

在前面就提到過AQS內部維護着一個FIFO的CLH隊列,所以AQS並不支持基於優先級的同步策略。至於為何要選擇CLH隊列,主要在於CLH鎖相對於MSC鎖,他更加容易處理cancel和timeout,同時他具備進出隊列快、無所、暢通無阻、檢查是否有線程在等待也非常容易(head != tail,頭尾指針不同)。當然相對於原始的CLH隊列鎖,ASQ采用的是一種變種的CLH隊列鎖:

1、原始CLH使用的locked自旋,而AQS的CLH則是在每個node里面使用一個狀態字段來控制阻塞,而不是自旋。

2、為了可以處理timeout和cancel操作,每個node維護一個指向前驅的指針。如果一個node的前驅被cancel,這個node可以前向移動使用前驅的狀態字段。

3、head結點使用的是傀儡結點。

2015112600001

3、共享鎖、互斥鎖

在AQS維護的CLH隊列鎖中,每個節點(Node)代表着一個需要獲取鎖的線程。該Node中有兩個常量SHARE、EXCLUSIVE。其中SHARE代表着共享模式,EXCLUSIVE代表着獨占模式。

static final class Node {
            /** Marker to indicate a node is waiting in shared mode */
            static final Node SHARED = new Node();
            /** Marker to indicate a node is waiting in exclusive mode */
            static final Node EXCLUSIVE = null;
            /////////

        }

其中共享模式是允許多個線程可以獲取同一個鎖,而獨占模式則一個鎖只能被一個線程持有,其他線程必須要等待。

4、阻塞、喚醒

我們知道在使用Java內置鎖時,可以使用wait、notify方法來阻塞、喚醒線程,但是AQS並沒有采用該模式,而是通過LockSupport.park() 和 LockSupport.unpark() 的本地方法來實現線程的阻塞和喚醒。


免責聲明!

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



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