AQS(AbstractQueuedSynchronizer)是 java.util.concurrent的基礎。J.U.C中宣傳的封裝良好的同步工具類Semaphore、CountDownLatch、ReentrantLock、ReentrantReadWriteLock、FutureTask等雖然各自都有不一樣特徵,可是簡單看一下源碼,每一個類內部都包含一個以下的內部類定義:html
abstract static class Sync extends AbstractQueuedSynchronizerjava
同時每一個類內部都包含有這樣一個屬性,連屬性名都同樣!註釋已經暗示了,該類的同步機制正是經過這個AQS的子類來完成的。不得不感嘆:「每一個強大的同步工具類,心裏都有一把一樣的鎖!」api
/** All mechanics via AbstractQueuedSynchronizer subclass */oracle
private
final
Sync sync;
函數
幾種同步類提供的功能其實都是委託sync來完成。有些是部分功能,有些則是所有功能。工具
AQS類中三個重要的屬性:ui
private transient volatile AbstractQueuedSynchronizer.Node head; private transient volatile AbstractQueuedSynchronizer.Node tail; private volatile int state;
Node類型的head和tail是一個FIFO的wait queue;一個int類型的狀態位state。到這裏也能猜到AQS對外呈現(或者說聲明)的主要行爲就是由一個狀態位和一個有序隊列來配合完成。 最簡單的讀一下主要的四個方法:spa
//獲取排他鎖 public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } //釋放排他鎖 public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; } //獲取共享鎖 public final void acquireShared(int arg) { if (tryAcquireShared(arg) < 0) doAcquireShared(arg); } //釋放共享鎖 public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { doReleaseShared(); return true; } return false; }
ReentrantLock對外的主要方法是lock(),tryLock()和unlock()方法,固然還有其餘變種的lockInterruptibly()、tryLock(long timeout, TimeUnit unit)等。線程
lock()方法的功能是獲取鎖。若是沒有線程使用則當即返回,並設置state爲1;若是當前線程已經佔有鎖,則state加1;若是其餘線程佔有鎖,則當前線程不可用,等待。code
public void lock() { sync.lock(); }
tryLock()的功能是:若是鎖可用,則獲取鎖,並當即返回值 true。若是鎖不可用,當即返回值 false。
public boolean tryLock() { return sync.nonfairTryAcquire(1); }
unlock()的功能是嘗試釋放鎖,若是當前線程佔有鎖則count減一,若是count爲0則釋放鎖。若佔有線程不是當前線程,則拋異常。
public void unlock() { sync.release(1); }
能夠看到也是藉助Sync來完成,咱們下面詳細看下Sync是如何實現這些」規定」的需求的。ReentrantLock的構造函數告訴咱們,其支持公平和非公平兩種鎖機制。
public ReentrantLock(boolean fair) { sync = (fair)? new FairSync() : new NonfairSync(); }
在該類中對應定了兩種FairSync和NonfairSync兩種同步器,都繼承者AQS。能夠看到對應執行的是lock、release、和Sync的nonfairTryAcquire。從前面AQS源碼知道release是在父類AQS中定義的方法,lock和nonfairTryAcquire是這個Sync中特定的方法,不是對父類對應方法的覆蓋。
lock方法有對於FairSync和NoFairSync有兩種不一樣的實現,對於非公平鎖只要當前沒有線程持有鎖,就將鎖給當前線程;而公平鎖不能這麼作,老是調用acquire方法來和其餘線程同樣公平的嘗試獲取鎖。
比較公平鎖機制和非公平鎖機制的差異僅僅在於若是當前沒有線程持有鎖,是優先把鎖分配給當前線程(非公平鎖),仍是優先分配給等待隊列中隊首的線程(公平鎖)。
ReentrantLock中定義的同步器分爲公平的同步器和非公平的同步器。在該同步器中state狀態位表示當前持有鎖的線程的重入次數。在獲取鎖時,經過覆蓋AQS的tryAcquire(int arg)方法,若是沒有線程持有鎖則當即返回,並設置state爲1;若是當前線程已經佔有鎖,則state加1;若是其餘線程佔有鎖,則當前線程不可用。釋放鎖時,覆蓋了AQS的tryRelease(int arg),在該方法中主要做用是state狀態位減小release個,表示釋放鎖,若是更新後的state爲0,表示當前線程釋放鎖,若是不爲0,表示持有鎖的當前線程重入數減小。
既然有一個請求計數器和一個佔有它的線程,即ReentrantLock實現了可重入鎖。
本文主要側重AQS的子類在各個同步工具類中的使用狀況,其實也基本涵蓋了這幾個同步工具類的主要邏輯,但目標並非對這幾個同步工具類的代碼進行詳細解析。另外AQS自己的幾個final方法,纔是同步器的公共基礎,也不是本文的主題,也未詳細展開。其實寫這篇文章的一個初始目的真的只是想列出以下表格,對比下AQS中的各個子類是怎麼使用state的,竟然囉嗦了這麼多。
工具類 | 工具類做用 | 工具類加鎖方法 | 工具類釋放鎖方法 | Sync覆蓋的方法 | Sync非覆蓋的重要方法 | state的做用 | 鎖類型 | 鎖維護 |
Semaphore | 控制同時訪問某個特定資源的操做數量 | acquire:每次請求一個許可都會致使計數器減小1,,一旦達到了0,新的許可請求線程將被掛起 | release:每調用 添加一個許可,釋放一個正在阻塞的獲取者 | tryAcquireShared tryReleaseShared | 初始化總共的許可數(同時最多能夠由幾個線程訪問) | 共享鎖 | 每一次請求acquire()一個許可都會致使計數器減小1,一樣每次釋放一個許可release()都會致使計數器增長1,一旦達到了0,新的許可請求線程將被掛起。 | |
CountDownLatch | 把一組線程所有關在外面,在某個狀態時候放開。一種同步機制來保證一個或多個線程等待其餘線程完成。 | await:在計數器不爲0時候阻塞調用線程,爲0時候當即返回 | countDown :計數遞減 | tryAcquireShared tryReleaseShared | 維護一個計數器 | 共享鎖 | 初始化一個計數,每次調用countDown方法計數遞減,在計數遞減到0以前,調用await的線程都會阻塞 | |
ReentrantLock | 標準的互斥操做,也就是一次只能有一個線程持有鎖 | lock:若是沒有線程使用則當即返回,並設置state爲1;若是當前線程已經佔有鎖,則state加1;若是其餘線程佔有鎖,則當前線程不可用,等待 tryLock:若是鎖可用,則獲取鎖,並當即返回值 true。若是鎖不可用,則此方法將當即返回值 false | unlock:嘗試釋放鎖,若是當前線程佔有鎖則count減一,若是count爲0則釋放鎖。若是佔有線程不是當前線程,則拋異常 | tryAcquire tryRelease | nonfairTryAcquire | state表示得到鎖的線程對鎖的重入次數。 | 排他鎖 | 獲取鎖時,若是沒有線程使用則當即返回,並設置state爲1;若是當前線程已經佔有鎖,則state加1;若是其餘線程佔有鎖,則當前線程不可用。釋放鎖時,在該方法中主要做用是state狀態位減小release個,表示釋放鎖,若是更新後的state爲0,表示當前線程釋放鎖,若是不爲0,表示持有鎖的當前線程重入數減小 |
ReentrantReadWriteLock | 讀寫鎖。容許多個讀線程同時持有鎖,可是隻有一個寫線程能夠持有鎖。寫線程獲取寫入鎖後能夠再次獲取讀取鎖,可是讀線程獲取讀取鎖後卻不能獲取寫入鎖 | ReadLock#lock :獲取讀鎖 ReadLock#tryLock:嘗試當前沒有其餘線程當前持有寫鎖時獲取讀鎖 WriteLock#lock:獲取寫鎖 WriteLock#tryLock:嘗試當前沒有其餘線程持有寫鎖時,呼氣寫鎖。 | ReadLock#unlock:釋放讀鎖 WriteLock#unlock:釋放寫鎖 | acquireShared releaseShared tryAcquire tryRelease | tryReadLock tryWriteLock | 高16位表示共享鎖的數量,低16位表示獨佔鎖的重入次數 | 讀鎖:共享 寫鎖:排他 |
對於共享鎖,state是計數器的概念。一個共享鎖就相對於一次計數器操做,一次獲取共享鎖至關於計數器加1,釋放一個共享鎖就至關於計數器減1;排他鎖維護相似於可重入鎖。 |
FutureTask | 封裝一個執行任務交給其餘線程去執行,開始執行後能夠被取消,能夠查看執行結果,若是執行結果未完成則阻塞。 | V get() | run() set(V) cancel(boolean) | tryAcquireShared tryReleaseShared | innerGet innerRun() innerSet innerIsCancelled | state狀態位來存儲執行狀態RUNNING、RUN、CANCELLED | 共享鎖 | 獲取執行結果的線程(能夠有多個)一直阻塞,直到執行任務的線程執行完畢,或者執行任務被取消。 |
參考: