ReentrantLock源碼分析筆記-單線程公平鎖爲例

前提

1)大致瞭解AQS原理(☆☆☆)

2)熟悉雙向鏈表的基本操作

3)本文以公平鎖上鎖釋放鎖爲例跟ReentrantLock代碼(☆☆☆)

4)本文以單線程搶佔鎖釋放鎖爲例(☆☆☆)

5)建議瞭解公平鎖和非公平鎖的區別

6)較好的理解能力(作者表達能力差)

AQS

AQS的核心思想(參考版)

如果被請求的共享資源空閒,則將當前請求資源的線程設置爲有效的工作線程,並將共享資源設置爲鎖定狀態,如果被請求的共享資源被佔用,那麼就需要一套線程阻塞等待以及被喚醒時鎖分配的機制,這個機制AQS是用CLH隊列鎖實現的,即將暫時獲取不到鎖的線程加入到隊列中。
CLH(Craig,Landin,and Hagersten)隊列是一個虛擬的雙向隊列,虛擬的雙向隊列即不存在隊列實例,僅存在節點之間的關聯關係。
AQS是將每一條請求共享資源的線程封裝成一個CLH鎖隊列的一個結點(Node),來實現鎖的分配。

用大白話來說,AQS就是基於CLH隊列,用volatile修飾共享變量state,線程通過CAS去改變狀態符,成功則獲取鎖成功,失敗則進入等待隊列,等待被喚醒。

**注意:AQS是自旋鎖:**在等待喚醒的時候,經常會使用自旋(while(!cas()))的方式,不停地嘗試獲取鎖,直到被其他線程獲取成功

實現了AQS的鎖有:自旋鎖、互斥鎖、讀鎖寫鎖、條件產量、信號量、柵欄都是AQS的衍生物

AQS的核心思想(作者版)

大致的思想是:

當一個線程發出上鎖的請求時,會看state狀態,

如果是0(沒有上鎖),則上鎖(state=1),處理業務邏輯;

如果不是0(別的線程佔有鎖),則把當前線程封裝成一個Node節點插入雙向鏈表中等待。

當上鎖的線程釋放鎖後會喚醒雙向鏈表中的第一個節點中的線程繼續使用鎖。

當然中間有很多細節,需要自己悟仔細悟,我只想說  Doug Lea NB.

public abstract class AbstractQueuedSynchronizer
        extends AbstractOwnableSynchronizer
        implements java.io.Serializable {

    /*當前持有鎖的線程,AbstractOwnableSynchronizer裏的屬性*/
    private transient Thread exclusiveOwnerThread;
 
    /*雙向鏈表的頭指針*/
    private transient volatile Node head;
 
    /*雙向鏈表的尾指針*/
    private transient volatile Node tail;
 
 
    /*鎖的狀態,1表示上鎖,0表示沒有鎖,大於1表示可重入鎖*/
    private volatile int state;
 
    /*雙向鏈表的Node節點*/
    static final class Node {
 
        /*每一個請求上鎖的線程都會封裝成一個雙向鏈表*/
        volatile Thread thread;
        /*雙向鏈表的prev指針*/
        volatile Node prev;
        /*雙向鏈表的next指針*/
        volatile Node next;
        
 
    }
}

測試代碼:單線程公平鎖爲例

圖解

上鎖

main函數

//一個線程上鎖釋放鎖,沒有任何併發情況
public class Start {
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock(true);
        System.out.println("--lock-");
        lock.lock();
        System.out.println("---logic--");
        lock.unlock();
        System.out.println("--unlock--");
 
    }
}

跟lock.lock()代碼(公平鎖),就跟到下面代碼

//FairSync 
final void lock() {
            //參數1很重要
            acquire(1);
}

繼續跟

//AbstractQueuedSynchronizer
public final void acquire(int arg) {//arg = 1
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

下面開始執行 tryAcquire(arg) 方法

//FairSync
protected final boolean tryAcquire(int acquires) {//acquires=1
            //獲取當前線程
            final Thread current = Thread.currentThread();
            //獲取lock的狀態(0表示沒有上鎖,1表示上鎖,大於1表示重入鎖)
            int c = getState();//c=0,因爲c是int類型,初始化爲0
            if (c == 0) {//走這個分支
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
               //不走這個分支
               //不走這個分支
               //不走這個分支
            }
            return false;
        }

 跟hasQueuedPredecessors()方法(這個方法賊難,賊難,賊難)

//AbstractQueuedSynchronizer
public final boolean hasQueuedPredecessors() {
        //獲取雙向鏈表的頭結點和尾結點,此時都爲null
        Node t = tail; 
        Node h = head;
        Node s;
        //h != t  返回 false,後面就不用執行了,整體返回false
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

tryAcquire(arg)裏的hasQueuedPredecessors()返回false,    !hasQueuedPredecessors() 返回true, 所以繼續跟compareAndSetState(0, acquires)方法

//AbstractQueuedSynchronizer
protected final boolean compareAndSetState(int expect, int update) { //expect=0,update=1
        //stateOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
        //修改stateOffset就是修改state
        // 本文的邏輯是在單線程情況下,所以cas會成功操作
        //即 return  true
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

此時tryAcquire方法裏!hasQueuedPredecessors() &&compareAndSetState(0, acquires)  整體是真,將執行setExclusiveOwnerThread(current)方法

//AbstractOwnableSynchronizer
protected final void setExclusiveOwnerThread(Thread thread) {
        //上面通過cas成功設置鎖的狀態,然後這裏將使用鎖的線程保存下來
        exclusiveOwnerThread = thread;
    }

acquire裏的tryAcquire返回true,   !tryAcquire  返回false, 即acquire執行完畢

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

------------------------------------------------>此時就是上鎖成功  (最顯著的結果就是將state從0變爲1,並且在一個變量AbstractOwnableSynchronizer中的exclusiveOwnerThread屬性保存了擁有鎖的線程)

釋放鎖

lock.unlock()跟到下面代碼

//ReentrantLock
public void unlock() {
        sync.release(1);
    }

繼續跟進release方法

//AbstractQueuedSynchronizer     
public final boolean release(int arg) {//GAG=1
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

跟tryRelease方法

//Sync
protected final boolean tryRelease(int releases) {
            //將state減少releases,此處state=1,releases=1
            int c = getState() - releases;
            //如果釋放鎖的線程不是當前線程,肯定不合適,誰上鎖誰釋放鎖
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            //如果c==0,說明鎖釋的狀態爲空閒,將當前上鎖的線程設置爲空
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            //修改新的state狀態
            setState(c);
            return free;
        }

------------------------------------------------>此時就是釋放鎖成功 (最顯著的結果就是將state從1變爲0,並且在一個變量AbstractOwnableSynchronizer中的exclusiveOwnerThread屬性設置爲null)

參考

https://blog.csdn.net/java_lyvee/article/details/98966684

https://ke.qq.com/course/455581?taid=4036264236348317

https://www.cnblogs.com/micrari/p/6937995.html

https://blog.csdn.net/mulinsen77/article/details/84583716

https://blog.csdn.net/qq_30572275/article/details/80297047

https://segmentfault.com/a/1190000015768003
https://blog.csdn.net/fxkcsdn/article/details/82217760

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章