下面會通過一步步的場景進行分析ReentrantLock公平鎖的源碼,文章為比較長,做好準備。。。
首先ReentrantLock默認創建的就是非公平鎖,所以要在ReentrantLock的構造函數中傳入true,創建公平鎖
ReentrantLock lock = new ReentrantLock(true);
lock.lock();
下面我們來一步步分析公平鎖進行加鎖 lock 的源碼實現:
注意,會通過一個個線程進入lock的形式進行分析;
當線程T1調用 lock():注意T1是第一個去競爭鎖的線程,后面的T2,T3。。默認都是在T1還沒執行完的情況下去分析的;
public voidlock() {
?? ?sync.lock();
}
繼而進入FairSync里面的lock()方法:
final voidlock() {
?? ?acquire(1);
}
接著進入acquire(1)方法,這里面才是重點:
public final voidacquire(intarg) {
?? ?if(!tryAcquire(arg) &&
?? ?acquireQueued(addWaiter(Node.EXCLUSIVE),arg))
?? ?selfInterrupt();
}
這個acquire(int arg)由四個方法構成:
tryAcquire:嘗試獲得鎖
addWaiter:將當前線程封裝成Node對象
acquireQueued:將當前線程表示的Node對象放到鏈表中進行排隊,同時park當前線程;
selfInterrupt:相應線程中斷;
代碼塊1 :分析acquire方法;
public final voidacquire(intarg) {
//首先會進入tryAcquire(arg)方法,看到這行注釋,請把目光移到下面代碼塊2中。。。
//代碼塊2 返回了true,而!tryAcquire(arg) 將返回結果取了反,也就是true變為了false,所以該方法結束,當前線程T1已經占有鎖;
?? ?if(!tryAcquire(arg) &&
?? ?? ??acquireQueued(addWaiter(Node.EXCLUSIVE),arg))
?? ??? ??? ?selfInterrupt();
}
代碼塊2 :
tryAcquire中的源碼:
protected final boolean tryAcquire(int acquires) {
//current是當前來獲取鎖的這個線程,也就是我們說的T1線程
????????final Thread current = Thread.currentThread();
? ? //state 表示鎖狀態,默認是0
//因為T1是第一個進來加鎖的,所以這里getState()得到的結果肯定等于0,所以 c = 0
????????int c = getState();
????????if (c == 0) {
?? ??? ?? ? //因為c=0,那么進入到這里
?? ??? ?? ? //hasQueuedPredecessors:作用主要是判斷當前AQS隊列中有沒有其他線程在排隊,具體看下面詳細說明,請將目光移到?代碼塊3
//通過分析代碼塊3,得知hasQueuedPredecessors會返回false,而!hasQueuedPredecessors()取了反,所以這里的結果變成了true,
?? ??? ?? ? //接著進入了compareAndSetState,進行CAS操作,將state改變為1,表示占有鎖 ,返回true
//compareAndSetState?返回了true ,進入到?setExclusiveOwnerThread,這里是將當前線程設置為占有鎖線程,用來支持鎖重入
?? ??? ?? ? //結果返回true,將true帶回到代碼塊1
????????????if (!hasQueuedPredecessors() &&
????????????????????compareAndSetState(0, acquires)) {
????????????????setExclusiveOwnerThread(current);
????????????????return true;
????????????}
????????}
????????else if (current == getExclusiveOwnerThread()) {
????????????int nextc = c + acquires;
????????????if (nextc < 0)
????????????????throw new Error("Maximum lock count exceeded");
????????????setState(nextc);
????????????return true;
????????}
????????return false;
????}
}
代碼塊3:
hasQueuedPredecessors中的源碼:
public final boolean hasQueuedPredecessors() {
? //因為T1是第一個進來加鎖的線程,那么這里的tail = null , head = null,因為還沒有線程去初始化他們,所以肯定是null;
????Node t = tail;
????Node h = head;
????Node s;
//h != t 相當于 head != tail 相當于 null != null,所以這里肯定返回的是false,下面的判斷就不用再看了,先分析T1進來的情況
????return h != t &&
????????((s = h.next) == null || s.thread != Thread.currentThread());
}
然后返回false;帶著false將目光回到代碼塊2
總結:
當第一個線程進來獲得鎖的時候,會將state由原來默認的0改為1,注意,此時并沒有初始化鏈表,也就是說,如果線程都是交替執行,那么永遠跟AQS隊列無關;
假設 T1 還沒有執行完,此時 T2 來了,也調用了 lock() ,T2 也要獲得鎖:
通過上面的分析,我們直接進入到這個方法:注意這里是T2線程,T1還沒有執行完,鎖還在T1那里
代碼塊1 :分析acquire方法;
public final voidacquire(intarg) {
? ? //首先會進入tryAcquire(arg)方法,看到這行注釋,請把目光移到下面代碼塊2中。。。
? ? //代碼塊2 返回了false,而?!tryAcquire(arg) 將返回結果取了反,也就是false變為了true,這邏輯將進入到acquireQueued(addWaiter(Node.EXCLUSIVE),arg) ,
//首先會先進入到addWaiter(Node.EXCLUSIVE),請將目光轉向代碼塊3中
//addWaiter(Node.EXCLUSIVE)經過代碼塊3和代碼塊4的分析,這里返回了當前線程T2表示的node對象,帶著這個返回的node進入到acquireQueued方法當中,帶著node 和 1,將目光移到代碼塊5
?? ?if(!tryAcquire(arg) &&
???? ??acquireQueued(addWaiter(Node.EXCLUSIVE),arg))
?? ??? ??? ?selfInterrupt();
}
代碼塊2 :
tryAcquire中的源碼:
protected final boolean tryAcquire(int acquires) {
? ? //當前線程,也就是T2線程
????final Thread current = Thread.currentThread();
? ? //因為T1線程還沒有執行完,所以T1線程還持有鎖,那么getState()返回的必然不是0,假設T1沒有重入,那么getState()返回的是1 , c = 1?
????int c = getState();
? ? // 因為 c = 1 ,所以邏輯不會進入到這里
????if (c == 0) {
????????if (!hasQueuedPredecessors() &&
????????????????compareAndSetState(0, acquires)) {
????????????setExclusiveOwnerThread(current);
????????????return true;
????????}
????}
// 因為 c = 1 所以來到這個判斷,getExclusiveOwnerThread返回的當前持有鎖的線程,也就是T1, current = T2,那么T2 == T1?當然不等于,返回false,帶著false回到代碼塊1中
????else if (current == getExclusiveOwnerThread()) {
????????int nextc = c + acquires;
????????if (nextc < 0)
????????????throw new Error("Maximum lock count exceeded");
????????setState(nextc);
????????return true;
????}
????return false;
}
代碼塊3:
private Node addWaiter(Node mode) {?? ?
? ? //將當前線程封裝為Node對象,這個Node當中維護了當前線程的狀態、前一個節點,后一個節點,當前線程的引用等等
????Node node = new Node(Thread.currentThread(), mode);?? ?
? ? //因為T1獲得鎖的時候,并沒有初始化AQS隊列,所以head節點和tail節點都是null,所以pred = tail = null;
????Node pred = tail;
? ? // pred = null ,所以這個條件不成立,將進入下面的enq方法
????if (pred != null) {
????????node.prev = pred;
????????if (compareAndSetTail(pred, node)) {
????????????pred.next = node;
????????????return node;
????????}
????}
? ? //當線程第一次初始化AQS隊列的時候,也就是隊列中還沒有任何等待的線程,enq方法很重要,請將目光移到代碼塊4:
????enq(node);
? ? //代碼塊4執行完,返回了當前線程T2表示的node節點,返回node,帶著node回到代碼塊1中
????return node;
}
代碼塊4:
private Node enq(final Node node) {
? ? //這里是個死循環
????for (;;) {
?? ?? ? //第一次進入循環,tail 肯定為 null ,因為隊列中沒有其他等待的線程,所以 t = tail = null
?? ?? ? //第二次進入循環,t = tail = head = new Node();
????????Node t = tail;
//第一次進入循環,t = null,所以會進入到里面的邏輯
?//第二次進入循環,t = tail = head = new Node(),也就是不等于null,所以進入到else 邏輯;
????????if (t == null) { // Must initialize
//第一次進入循環,當tail = null 的時候,說明隊列還沒有被初始化,也就是AQS還沒有任何線程進來排隊過,通過compareAndSetHead 為 head 創建一個空Node,哨兵節點
//第一次進入循環,同時tail = head = new Node(),接著進入下一次循環
????????????if (compareAndSetHead(new Node()))
????????????????tail = head;
????????} else {
?? ??? ?? ? //第二次循環會進入到這里,因為第一次循環中創建了哨兵節點head
?? ??? ?? ? // node.prev = t :當前節點的前驅節點指向 t = tail = head,也就是指向head節點
????????????node.prev = t;
?? ??? ?? ? // 通過CAS將當前T2線程表示的節點node設置tail節點
????????????if (compareAndSetTail(t, node)) {
?? ??? ??? ?? ? //t.next = node ,t = head,所以將head節點的后繼節點指向當前T2線程表示的節點
????????????????t.next = node;
?? ??? ??? ?//返回當前節點,帶著當前線程T2表示的node,返回到代碼塊3中
????????????????return t;
????????????}
????????}
????}
}
代碼塊5:
final boolean acquireQueued(final Node node, int arg) {
????boolean failed = true;
????try {
????????boolean interrupted = false;
?? ?? ? //這里是一個死循環
????????for (;;) {
?? ??? ?? ? //第一次循環,獲得當前節點的前驅節點,也就是head節點
?? ??? ?? ? //第二次循環,獲得當前節點的前驅節點,也就是head節點
????????????final Node p = node.predecessor();
//第一次循環,p == head ? 當然等于,所以進入tryAcquire嘗試獲得鎖,因為前面的處理過程中,可能T1已經執行完了,這里其實也相當于自旋,我們假設T1還沒有執行完,這里沒有獲取到鎖返回false
//第二次循環,P == head ? 當然等于,所以再次進入到tryAcquire中嘗試獲得鎖,又一次自旋,假設還是沒有獲得到鎖,因為T1還沒有執行完,鎖還沒有被釋放
????????????if (p == head && tryAcquire(arg)) {
????????????????setHead(node);
????????????????p.next = null; // help GC
????????????????failed = false;
????????????????return interrupted;
????????????}
?? ??? ?//第一次循環,帶著當前節點node的前驅節點head,和當前節點node,進入到shouldParkAfterFailedAcquire方法之中,這里很重要,請看代碼塊6
//第一次循環,從代碼塊6中的shouldParkAfterFailedAcquire返回了false,那么第一次循環結束
//第二次循環,帶著當前節點node的前驅節點head,和當前節點node,再次進入到shouldParkAfterFailedAcquire方法之中,請看代碼塊6
//第二次循環,從代碼塊6中的shouldParkAfterFailedAcquire返回了true,代表著將進入parkAndCheckInterrupt方法將當前線程休眠,等待被喚醒,代碼阻塞在里面,至此,T2進入睡眠;
????????????if (shouldParkAfterFailedAcquire(p, node) &&
????????????????????parkAndCheckInterrupt())
????????????????interrupted = true;
????????}
????} finally {
????????if (failed)
????????????cancelAcquire(node);
????}
}
代碼塊6:
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
????//外面第一次循環:pred = head , node 為 T2,因為T2剛剛才初始化了head節點,所以head節點中的waitStatus = 0;
? ? //外面第一次循環:ws = 0 ;
? ? //外面第二次循環:因為在第一次的時候,我們已經將head節點中waitStatus改為-1
????int ws = pred.waitStatus;
? ? //外面第一次循環:ws == Node.SIGNAL == -1?ws = 0,所以不會進入到這里面
? ? //外面第二次循環:ws == Node.SIGNAL == -1?ws = -1,所以會進入到這里面
????if (ws == Node.SIGNAL)
????????/*
???????? * This node has already set status asking a release
???????? * to signal it, so it can safely park.
???????? */
?? ?? ? //返回true,帶著這個true,回到代碼塊5中
????????return true;
????if (ws > 0) {
????????/*
???????? * Predecessor was cancelled. Skip over predecessors and
???????? * indicate retry.
???????? */
????????do {
????????????node.prev = pred = pred.prev;
????????} while (pred.waitStatus > 0);
????????pred.next = node;
????} else {
//外面第一次循環:會來到這里,通過CAS將head節點中的waitStatus 改 -1,然后返回false,帶著false回到代碼塊5中
????????compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
????}
????return false;
}
總結:T2在經過一次獲取鎖,兩次自旋后還是沒有獲得到鎖,初始化了head哨兵節點,并將head節點中的waitStatus = -1 ,T2進入了睡眠,等待喚醒;
此時,T1還是沒有執行完,T3又來競爭鎖:
代碼塊1:
public final void acquire(int arg) {
//還是通過tryAcquire嘗試獲得鎖,這里不在分析tryAcquire,因為T1還沒有被執行完,T2正在睡眠,tryAcquire還是沒有獲得到鎖,返回false,則進入代碼塊2
//代碼塊2中,addWaiter成功將當前T3表示的node加入到鏈表尾部,并返回當前node
? ? //帶著當前線程T3表示的node進入到acquireQueued,這里面其實就是自旋和當前是否休眠的操作,請看代碼塊3
????if (!tryAcquire(arg) &&
????????acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
????????selfInterrupt();
}
代碼塊2:
private Node addWaiter(Node mode) {?? ?
? ? //將當前線程封裝為Node對象,這個Node當中維護了當前線程的狀態、前一個節點,后一個節點,當前線程的引用等等
????Node node = new Node(Thread.currentThread(), mode);?? ?
? ? //因為T2剛剛已經初始化了AQS隊列,并創建了head哨兵節點,同時將tail 設置了等于T2線程表示的node,所以tail = T2表示的node;
????Node pred = tail;
? ? // pred = T2node? ,所以這個條件成立,將進入方法
????if (pred != null) {
?? ?? ? //將當前節點的前驅節點指向T2表示的node
????????node.prev = pred;
?? ?? ? //將tail表示的節點等于當前節點T3表示的node,相當于T3表示node是尾節點
????????if (compareAndSetTail(pred, node)) {
?? ??? ?? ? //pred = T2表示的node,所以這里是將T2表示的node的下一個節點指向當前T3表示的node,這就是一個雙向鏈表的數據結構,即使后面T4,T5。。。也是這樣鏈接起來
????????????pred.next = node;
// 返回當前node,帶著node回到代碼塊1中
????????????return node;
????????}
????}
? ? //當線程第一次初始化AQS隊列的時候,也就是隊列中還沒有任何等待的線程,enq方法很重要,請將目光移到代碼塊4:
????enq(node);
? ? //代碼塊4執行完,返回了當前線程T2表示的node節點,返回node,帶著node回到代碼塊1中
????return node;
}
代碼塊3:
final boolean acquireQueued(final Node node, int arg) {
????boolean failed = true;
????try {
????????boolean interrupted = false;
?? ?? ? //這里是一個死循環
????????for (;;) {
?? ??? ?? ? //第一次循環,獲得當前節點的前驅節點,也就是T2表示的node節點
?? ??? ?//第二次循環,獲得當前節點的前驅節點,也就是T2表示的node節點
????????????final Node p = node.predecessor();
?? ??? ?? ? //第一次循環,p == head ? 當然不等于,因為現在當前節點的前驅節點是T2表示的node節點,直接返回了false,進入到下面的shouldParkAfterFailedAcquire當中,請看代碼塊4
?? ??? ?? ? //第二次循環,P == head ? 當然不等于,所以再次進入到tryAcquire中嘗試獲得鎖,又一次自旋,假設還是沒有獲得到鎖,因為T1還沒有執行完,鎖還沒有被釋放
????????????if (p == head && tryAcquire(arg)) {
????????????????setHead(node);
????????????????p.next = null; // help GC
????????????????failed = false;
????????????????return interrupted;
????????????}
?? ??? ?? ? //第一次循環,帶著當前節點node的前驅節點head,和當前節點node,進入到shouldParkAfterFailedAcquire方法之中,這里很重要,請看代碼塊4
?? ??? ?? ? //第一次循環,從代碼塊4中的shouldParkAfterFailedAcquire返回了false,那么第一次循環結束
?? ??? ?? ? //第二次循環,帶著當前節點T3表示的node的前驅節點T2 node,和當前節點T3 node,再次進入到shouldParkAfterFailedAcquire方法之中,請看代碼塊4
?? ??? ?? ? //第二次循環,從代碼塊4中的shouldParkAfterFailedAcquire返回了true,代表著將進入parkAndCheckInterrupt方法將當前線程休眠,等待被喚醒,代碼阻塞在里面,至此,T3進入睡眠;
????????????if (shouldParkAfterFailedAcquire(p, node) &&
????????????????????parkAndCheckInterrupt())
????????????????interrupted = true;
????????}
????} finally {
????????if (failed)
????????????cancelAcquire(node);
????}
}
代碼塊4:
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
????//外面第一次循環:pred = T2表示的node , node 為 T2,因為T2剛剛將head節點的waitStatus = -1之后自己進入了睡眠,但是T2的waitStatus還是等于0;
? ? //外面第一次循環:ws = 0 ;
? ? //外面第二次循環:因為在第一次的時候,我們已經將T2 node節點中waitStatus改為-1
????int ws = pred.waitStatus;
? ? //外面第一次循環:ws == Node.SIGNAL == -1?ws = 0,所以不會進入到這里面
? ? //外面第二次循環:ws == Node.SIGNAL == -1?ws = -1,所以會進入到這里面
????if (ws == Node.SIGNAL)
????????/*
???????? * This node has already set status asking a release
???????? * to signal it, so it can safely park.
???????? */
?? ?? ? //返回true,帶著這個true,回到代碼塊3中
????????return true;
????if (ws > 0) {
????????/*
???????? * Predecessor was cancelled. Skip over predecessors and
???????? * indicate retry.
???????? */
????????do {
????????????node.prev = pred = pred.prev;
????????} while (pred.waitStatus > 0);
????????pred.next = node;
????} else {
?? ?? ? //外面第一次循環:會來到這里,通過CAS將head節點中的waitStatus 改 -1,然后返回false,帶著false回到代碼塊5中
????????compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
????}
????return false;
}
總結:T3 和 T2的過程差不多,只是T2多了個初始化head節點的操作;
至此,當前AQS中,T1正在實行,T2 ,T3正在隊列中睡眠,這就是當線程競爭鎖不到時排隊的情景;
下面我們來看看另外一個場景:
假設T2在獲取不到鎖的時候,準備加入隊列中的時候,T1執行完了,釋放了鎖,這個時候是怎樣的情況?
代碼塊1:
public final void acquire(int arg) {
//當T1未執行完時,T2線程來了,并且tryAcquire在嘗試獲得鎖失敗,在經過addWaiter封裝成了node對象進入acquireQueued準備排隊,我們來看看此時acquireQueued的情況
????if (!tryAcquire(arg) &&
????????????acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
????????selfInterrupt();
}
代碼塊2:
final boolean acquireQueued(final Node node, int arg) {
????boolean failed = true;
????try {
????????boolean interrupted = false;
????????for (;;) {
?? ??? ?//由于在addWaiter中已經初始化了head節點,當前node排在head之后
?? ??? ?? ? // p = head = node.predecessor()
????????????final Node p = node.predecessor();
?? ??? ?// p == head ? 明顯是等于的,這個時候進入tryAcquire嘗試獲得鎖,在這之前T1執行完了,所以這里T2獲得到了鎖,返回true,進入
????????????if (p == head && tryAcquire(arg)) {
?? ??? ??? ?? ? //將當前節點node設置為head節點,因為他已經獲得鎖了,不用在排隊了,所以把head=null,讓GC回收,當前節點node變為了head
?? ??? ??? ?? ? //setHead當中做了三個事情:1.將head等于當前node,2.將當前節點node的前驅節點=null,3.將當前節點的thread等于null
????????????????setHead(node);
?? ??? ??? ?? ? //因為當前節點node已經變為了head,所以head的next也是等于空
????????????????p.next = null; // help GC
????????????????failed = false;
????????????????return interrupted;
????????????}
????????????if (shouldParkAfterFailedAcquire(p, node) &&
????????????????????parkAndCheckInterrupt())
????????????????interrupted = true;
????????}
????} finally {
????????if (failed)
????????????cancelAcquire(node);
????}
}
以上就是ReentrantLock之公平鎖的加鎖過程,后續我們會分析加鎖過程
總結:
1.公平鎖在調用lock的時候總共嘗試獲得鎖幾次?
答:
1.如果是第一個線程,則一次就獲得成功
2.如果是第二個線程,第一個線程還沒有執行完,那么會經過三次嘗試獲得鎖,在得不到鎖的情況進入休眠