【JDK源碼分析系列】AbstractQueuedSynchronizer 源碼分析 -- 基本屬性

【JDK源碼分析系列】AbstractQueuedSynchronizer 源碼分析 -- 基本屬性

【1】AbstractQueuedSynchronizer 整體架構

1. AQS 中隊列只有兩個 : 同步隊列 + 條件隊列,底層數據結構兩者都是鏈表
2. 圖中有四種顏色的線代表四種不同的場景,1、2、3 序號代表看的順序

【2】AbstractQueuedSynchronizer 基本屬性

【2.0】AbstractQueuedSynchronizer 的定義與說明

// 1. 提供了一種框架,自定義了先進先出的同步隊列,讓獲取不到鎖的線程能進入同步隊列中排隊;
// 2. 同步器有個狀態字段,可以通過狀態字段來判斷能否得到鎖,此時設計的關鍵在於依賴安全的 atomic value 來表示狀態
//   (雖然註釋是這個意思,但實際上是通過把狀態聲明爲 volatile,在鎖裏面修改狀態值來保證線程安全的);
// 3. 子類可以通過給狀態 CAS 賦值來決定能否拿到鎖,可以定義那些狀態可以獲得鎖,哪些狀態表示獲取不到鎖
//   (比如定義狀態值是 0 可以獲得鎖,狀態值是 1 就獲取不到鎖);
// 4. 子類可以新建非 public 的內部類,用內部類來繼承 AQS,從而實現鎖的功能;
// 5. AQS 提供了排它模式和共享模式兩種鎖模式,排它模式下:只有一個線程可以獲得鎖,
//    共享模式可以讓多個線程獲得鎖,子類 ReadWriteLock 實現了兩種模式;
// 6. 內部類 ConditionObject 可以被用作 Condition,通過 new ConditionObject () 即可得到條件隊列;
// 7. AQS 實現了鎖、排隊、鎖隊列等框架,至於如何獲得鎖、釋放鎖的代碼並沒有實現,
//      比如 tryAcquire、tryRelease、tryAcquireShared、tryReleaseShared、isHeldExclusively 這些方法,
//      AQS 中默認拋 UnsupportedOperationException 異常,都是需要子類去實現的;
// 8. AQS 繼承 AbstractOwnableSynchronizer 是爲了方便跟蹤獲得鎖的線程,可以幫助監控和診斷工具識別是哪些線程持有了鎖;
// 9. AQS 同步隊列和條件隊列,獲取不到鎖的節點在入隊時是先進先出,但被喚醒時,可能並不會按照先進先出的順序執行;

// AQS 是個抽象類,就是給各種鎖子類繼承用的,AQS 定義了很多如何獲得鎖,如何釋放鎖的抽象方法,目的就是爲了讓子類去實現;
// 繼承了 AbstractOwnableSynchronizer,AbstractOwnableSynchronizer 的作用
// 就是爲了知道當前是那個線程獲得了鎖,方便監控用的;
// AbstractOwnableSynchronizer 類中 private transient Thread exclusiveOwnerThread; 屬性用於描述當前獲得鎖的線程
// setExclusiveOwnerThread 方法用於設置當前獲取鎖的線程
// protected final void setExclusiveOwnerThread(Thread thread) {
//     exclusiveOwnerThread = thread;
// }

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

【2.1】AbstractQueuedSynchronizer 簡單屬性

// 同步器的狀態,根據當前狀態進行判斷是否可以獲得當前鎖
// 如果當前state是0,那麼可以獲得鎖
// 可重入鎖,每次獲得鎖+1,每次釋放鎖-1
private volatile int state;

// 自旋超時閥值,單位納秒
// 當設置等待時間時纔會用到這個屬性
static final long spinForTimeoutThreshold = 1000L;

【2.2】AbstractQueuedSynchronizer 同步隊列屬性

同步隊列:底層數據結構是一個雙向鏈表,當多個線程都來請求鎖時,某一時刻有且只有一個線程能夠獲得鎖 (排它鎖),那麼剩餘獲取不到鎖的線程,都會到同步隊列中去排隊並阻塞自己,當有線程主動釋放鎖時,就會從同步隊列頭開始釋放一個排隊的線程,讓線程重新去競爭鎖,所以同步隊列的主要作用阻塞獲取不到鎖的線程,並在適當時機釋放這些線程。

// 同步隊列的頭。
// 公平的鎖先入先出。
private transient volatile Node head;

// 等待隊列的尾
private transient volatile Node tail;

【2.3】AbstractQueuedSynchronizer 條件隊列屬性

條件隊列:條件隊列管理獲取不到鎖的線程,底層數據結構也是鏈表隊列,但條件隊列不直接和鎖打交道,但常常和鎖配合使用,是一定的場景下,對鎖功能的一種補充。

private static final long serialVersionUID = 1173984872572414699L;
// 條件隊列中第一個 node
private transient Node firstWaiter;
// 條件隊列中最後一個 node
private transient Node lastWaiter;

【2.4】AbstractQueuedSynchronizer Node 屬性

//Node 既是同步隊列的節點,又是條件隊列的節點,
//在入隊的時候,用 Node 把線程包裝一下,然後把 Node 放入兩個隊列中
static final class Node {

    //node 是共享模式
    /** 標誌用於指示一個節點在共享模式下等待 */
    static final Node SHARED = new Node();

    //node 是排它模式
    /** 標誌用於指示一個節點在獨佔模式下等待 */
    static final Node EXCLUSIVE = null;


    /** 
    * 等待狀態值,表示線程被取消
    *
    * 由於在同步隊列中等待的線程等待超時或中斷,需要從同步隊列中取消等待
    * 節點進入該狀態後不會發生變化
    */
    // 被取消
    static final int CANCELLED =  1;

    /**
     * 後繼結點的線程處於等待狀態,而當前節點的線程如果釋放了同步狀態或者被取消,
     * 將會通知後繼節點,使後繼節點的線程得以運行
     */
    // SIGNAL 狀態的意義:同步隊列中的節點在自旋獲取鎖的時候,
    // 如果前一個節點的狀態是 SIGNAL,那麼自己就可以阻塞休息了,否則自己一直自旋嘗試獲得鎖
    static final int SIGNAL    = -1;

    /**
     * 節點在等待隊列中,節點線程等待在Condition上,
     * 當其他線程對Condition調用了Signal()方法後,
     * 該節點將會從等待隊列轉移到同步隊列中,加入到對同步狀態的獲取中
     */
    // 表示當前 node 正在條件隊列中,
    // 當有節點從同步隊列轉移到條件隊列時,狀態就會被更改成 CONDITION
    static final int CONDITION = -2;
    /**
     * 表示下一次共享式同步狀態獲取將會無條件地被傳播下去
     */
    // 無條件傳播,共享模式下,該狀態的進程處於可運行狀態
    static final int PROPAGATE = -3;

    /**
     * 等待狀態變量
     *
     * volatile 作用簡介 : java 線程內存模型將確保所有線程看到的該變量是一致的;
     */
    // 表示當前節點的狀態,通過節點的狀態來控制節點的行爲
    // 普通同步節點,就是 0 ,條件節點是 CONDITION -2
    volatile int waitStatus;

    /**
    * 指向同步隊列中當前節點的前驅節點
    */
    // 當前節點的前節點
    // 節點 acquire 成功後就會變成 head
    // head 節點不能被 cancelled
    volatile Node prev;

    /**
    * 指向同步隊列中當前節點的後繼節點
    */
    // 當前節點的下一個節點
    volatile Node next;

    // 當前節點的線程
    volatile Thread thread;

    // 在同步隊列中,nextWaiter 並不真的是指向其下一個節點,我們用 next 表示同步隊列的下一個節點,
    // nextWaiter 只是表示當前 Node 是排它模式還是共享模式
    // 但在條件隊列中,nextWaiter 就是表示下一個節點元素
    Node nextWaiter;

    /**
     * 該方法用於判斷節點是否處於共享狀態
     */
    final boolean isShared() {
        return nextWaiter == SHARED;
    }

    /**
     * 該方法用於獲取當前節點的前驅節點
     * 在該方法中添加了空指針判斷,在前驅節點爲空時,拋出空指針異常;
     */
    final Node predecessor() throws NullPointerException {
        Node p = prev;
        if (p == null)
            throw new NullPointerException();
        else
            return p;
    }

    /**
     * 內部類 Node 的默認構造方法
     * 
     * 用於創立初始化頭結點或者共享節點
     */
    Node() {
    }
    /**
     * 內部類 Node 的構造方法
     *
     * 構造的節點指定等待隊列中的節點
     */
    Node(Thread thread, Node mode) {
        this.nextWaiter = mode;
        this.thread = thread;
    }
    /**
     * 內部類 Node 的構造方法
     *
     * 構造的節點指定等待的狀態
     */        
    Node(Thread thread, int waitStatus) {
        this.waitStatus = waitStatus;
        this.thread = thread;
    }
}

【2.5】AbstractQueuedSynchronizer 一般方法

【2.5.0】AbstractQueuedSynchronizer 隊列操作圖示

【2.5.1】AbstractQueuedSynchronizer 相關 CAS 方法

/**
 * CAS 方式設置同步狀態;
 */
protected final boolean compareAndSetState(int expect, int update) {
    
    /**
     * Unsafe 類提供了硬件級別的原子操作,提供了一些繞開JVM的更底層功能,由此提高效率;
     * compareAndSwapInt : 比較並交換 int 型變量
     *
     * 參數說明
     * 參數包含 : 需要讀寫的內存位置(stateOffset)、進行比較的預期原值(expect)
     * 和擬寫入的新值(update);
     * 處理方式 : 如果內存位置stateOffset的值與預期原值expect相匹配,
     * 那麼處理器會自動將該位置值更新爲新值update;
     * 否則處理器不做任何操作。
     */
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

/**
 * CAS head field. Used only by enq.
 */
private final boolean compareAndSetHead(Node update) {
    return unsafe.compareAndSwapObject(this, headOffset, null, update);
}

/**
 * CAS tail field. Used only by enq.
 */
private final boolean compareAndSetTail(Node expect, Node update) {
    return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}

/**
 * CAS waitStatus field of a node.
 */
private static final boolean compareAndSetWaitStatus(Node node,
                                                        int expect,
                                                        int update) {
    return unsafe.compareAndSwapInt(node, waitStatusOffset,
                                    expect, update);
}

/**
 * CAS next field of a node.
 */
private static final boolean compareAndSetNext(Node node,
                                                Node expect,
                                                Node update) {
    return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
}

【2.5.2】AbstractQueuedSynchronizer 相關線程處理方法

private final boolean parkAndCheckInterrupt() {
    //阻塞當前線程
    LockSupport.park(this);
    //中斷當前線程
    return Thread.interrupted();
}

【3】條件 (Condition)

【3.0】Condition 的定義與說明

// 1. 當 lock 代替 synchronized 來加鎖時,Condition 就可以用來代替 Object 中相應的監控方法了,
//  比如 Object#wait ()、Object#notify、Object#notifyAll 這些方法
// 2. 提供了一種線程協作方式:一個線程被暫停執行,直到被其它線程喚醒
// 3. Condition 實例是綁定在鎖上的,通過 Lock#newCondition 方法可以產生該實例
// 4. 除了特殊說明外,任意空值作爲方法的入參,都會拋出空指針
// 5. Condition 提供了明確的語義和行爲,這點和 Object 監控方法不同
public interface Condition {

【3.1】Object 的監視器方法與 Condition 接口對比

【3.2】Condition 常用方法以及描述

 參考致謝
本博客爲博主的學習實踐總結,並參考了衆多博主的博文,在此表示感謝,博主若有不足之處,請批評指正。
【1】Java併發編程的藝術

【2】面試官系統精講Java源碼及大廠真題/30 AbstractQueuedSynchronizer 源碼解析(上)

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