AQS:AbstractQuenedSynchronizer抽象的隊列式同步器,這個類在java.util.concurrent.locks包下,是JAVA除了自帶的synchronized關鍵字以外的鎖機制實現。AQS中維護了一個 volatile int state 變量(表明共享資源)和一個CLH隊列(多線程爭搶資源失敗被阻塞時進入的FIFO隊列)。java
AQS的核心思想是,若是被請求的共享資源空閒(state==0),則將當前請求資源的線程設置爲有效的工做線程,並將共享資源設置爲鎖定狀態,若是被請求的共享資源被佔用,那麼就將暫時獲取不到鎖的線程加入到CLH隊列中,進行線程阻塞,並等待喚醒。
多線程
CLH(Craig,Landin,and Hagersten)隊列是一個虛擬的雙向隊列,虛擬的雙向隊列即不存在隊列實例,僅存在節點之間的關聯關係。
AQS是將每一條請求共享資源的線程封裝成一個CLH鎖隊列的一個結點(Node),來實現鎖的分配。
線程的阻塞、喚醒利用的是LockSupport的park、unpark方法。
說的再白點就是,AQS基於CLH隊列,用volatile修飾共享變量state,線程經過CAS去改變狀態符,成功則獲取鎖成功,失敗則進入等待隊列,等待被喚醒。
注意:AQS是自旋鎖,在等待喚醒的時候,常常會使用自旋(while(!cas()))的方式,不停地嘗試獲取鎖,直到被其餘線程獲取成功。編輯器
實現了AQS的鎖有:自旋鎖、互斥鎖、讀鎖寫鎖、條件產量、信號量、柵欄都是AQS的衍生物
AQS實現的具體方式以下:ide
如圖示,AQS維護了一個volatile int state和一個FIFO線程等待隊列,多線程爭用資源被阻塞的時候就會進入這個隊列。state就是共享資源,其訪問方式有以下三種:
getState();setState();compareAndSetState();
AQS 定義了兩種資源共享方式:
1.Exclusive:獨佔,只有一個線程能執行,如ReentrantLock
2.Share:共享,多個線程能夠同時執行,如Semaphore、CountDownLatch、ReadWriteLock,CyclicBarrier
不一樣的自定義的同步器爭用共享資源的方式也不一樣。
AQS底層是用來模板方法模式:同步器的設計是基於模板方法模式的,若是須要自定義同步器通常的方式是這樣(模板方法模式很經典的一個應用):
使用者繼承AbstractQueuedSynchronizer並重寫指定的方法。(這些重寫方法很簡單,無非是對於共享資源state的獲取和釋放)
將AQS組合在自定義同步組件的實現中,並調用其模板方法,而這些模板方法會調用使用者重寫的方法。
這和咱們以往經過實現接口的方式有很大區別,這是模板方法模式很經典的一個運用。
自定義同步器在實現的時候只須要實現共享資源state的獲取和釋放方式便可,至於具體線程等待隊列的維護,AQS已經在頂層實現好了。自定義同步器實現的時候主要實現下面幾種方法:
isHeldExclusively():該線程是否正在獨佔資源。只有用到condition才須要去實現它。
tryAcquire(int):獨佔方式。嘗試獲取資源,成功則返回true,失敗則返回false。
tryRelease(int):獨佔方式。嘗試釋放資源,成功則返回true,失敗則返回false。
tryAcquireShared(int):共享方式。嘗試獲取資源。負數表示失敗;0表示成功,但沒有剩餘可用資源;正數表示成功,且有剩餘資源。
tryReleaseShared(int):共享方式。嘗試釋放資源,若是釋放後容許喚醒後續等待結點返回true,不然返回false。
以ReentrantLock爲例,(可重入獨佔式鎖):state初始化爲0,表示未鎖定狀態,A線程lock()時,會調用tryAcquire()獨佔鎖並將state+1.以後其餘線程再想tryAcquire的時候就會失敗,直到A線程unlock()到state=0爲止,其餘線程纔有機會獲取該鎖。A釋放鎖以前,本身也是能夠重複獲取此鎖(state累加),這就是可重入的概念。
注意:獲取多少次鎖就要釋放多少次鎖,保證state是能回到零態的。
以CountDownLatch爲例,任務分N個子線程去執行,state就初始化 爲N,N個線程並行執行,每一個線程執行完以後countDown()一次,state就會CAS減一。當N子線程所有執行完畢,state=0,會unpark()主調用線程,主調用線程就會從await()函數返回,繼續以後的動做。
通常來講,自定義同步器要麼是獨佔方法,要麼是共享方式,他們也只需實現tryAcquire-tryRelease、tryAcquireShared-tryReleaseShared中的一種便可。但AQS也支持自定義同步器同時實現獨佔和共享兩種方式,如ReentrantReadWriteLock。在acquire() acquireShared()兩種方式下,線程在等待隊列中都是忽略中斷的,acquireInterruptibly()/acquireSharedInterruptibly()是支持響應中斷的。函數
咱們本身實現個不可重入的Lock實例,上代碼:ui
public class NonReentrantLock implements Lock, Serializable { private static final long serialVersionUID = 3642178150160509120L; private Sync sync; public NonReentrantLock() { this.sync = new Sync(); } private class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = 5297723527637891456L; @Override protected boolean tryAcquire(int arg) { if (compareAndSetState(0, arg)) { setExclusiveOwnerThread(Thread.currentThread()); return true; } else if (isHeldExclusively()) { throw new UnsupportedOperationException("該鎖不支持重入!"); } return false; } @Override protected boolean tryRelease(int arg) { return compareAndSetState(arg, 0); } @Override protected boolean isHeldExclusively() { return getExclusiveOwnerThread() == Thread.currentThread(); } private Condition newCondition() { return new ConditionObject(); } } @Override public void lock() { sync.acquire(1); } @Override public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); } @Override public boolean tryLock() { return sync.tryAcquire(1); } @Override public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(time)); } @Override public void unlock() { sync.release(1); } @Override public Condition newCondition() { return sync.newCondition(); } }
總結:AQS爲Lock的實現提供了底層基礎,並經過模板方法模式解決了複雜的線程入隊阻塞、喚醒的操做,實現者只須要根據本身的特定需求重寫某些方法便可 this