Java併發程式設計解析 | 基於JDK原始碼解析Java領域中ReentrantLock鎖的設計思想與實現原理 (一)

語言: CN / TW / HK

蒼穹之邊,浩瀚之摯,眰恦之美; 悟心悟性,善始善終,惟善惟道! —— 朝槿《朝槿兮年說》

寫在開頭

在併發程式設計領域,有兩大核心問題:一個是互斥,即同一時刻只允許一個執行緒訪問共享資源;另一個是同步,即執行緒之間如何通訊、協作。<br />主要原因是,對於多執行緒實現實現併發,一直以來,多執行緒都存在2個問題:

  • 執行緒之間記憶體共享,需要通過加鎖進行控制,但是加鎖會導致效能下降,同時複雜的加鎖機制也會增加程式設計編碼難度
  • 過多執行緒造成執行緒之間的上下文切換,導致效率低下

因此,在併發程式設計領域中,一直有一個很重要的設計原則: “ 不要通過記憶體共享來實現通訊,而應該通過通訊來實現記憶體共享。”<br />簡單來說,就是儘可能通過訊息通訊,而不是記憶體共享來實現程序或者執行緒之間的同步。

關健術語

<br />本文用到的一些關鍵詞語以及常用術語,主要如下:

  • 併發(Concurrent): 在作業系統中,是指一個時間段中有幾個程式都處於已啟動執行到執行完畢之間,且這幾個程式都是在同一個處理機上執行。
  • 並行(Parallel): 當系統有一個以上CPU時,當一個CPU執行一個程序時,另一個CPU可以執行另一個程序,兩個程序互不搶佔CPU資源,可以同時進行。
  • 訊號量(Semaphore):  是在多執行緒環境下使用的一種設施,是可以用來保證兩個或多個關鍵程式碼段不被併發呼叫,也是作系統用來解決併發中的互斥和同步問題的一種方法。
  • 訊號量機制(Semaphores): 用來解決同步/互斥的問題的,它是1965年,荷蘭學者 Dijkstra提出了一種卓有成效的實現程序互斥與同步的方法。
  • 管程(Monitor) :  一般是指管理共享變數以及對共享變數的操作過程,讓它們支援併發的一種機制。
  • 互斥(Mutual Exclusion):一個公共資源同一時刻只能被一個程序或執行緒使用,多個程序或執行緒不能同時使用公共資源。即就是同一時刻只允許一個執行緒訪問共享資源的問題。
  • 同步(Synchronization):兩個或兩個以上的程序或執行緒在執行過程中協同步調,按預定的先後次序執行。即就是執行緒之間如何通訊、協作的問題。
  • 物件池(Object Pool): 指的是一次性創建出 N 個物件,之後所有的執行緒重複利用這 N 個物件,當然物件在被釋放前,也是不允許其他執行緒使用的, 一般指儲存例項物件的容器。

基本概述

在Java領域中,我們可以將鎖大致分為基於Java語法層面(關鍵詞)實現的鎖和基於JDK層面實現的鎖。

在Java領域中, 尤其是在併發程式設計領域,對於多執行緒併發執行一直有兩大核心問題:同步和互斥。其中:

  • 互斥(Mutual Exclusion):一個公共資源同一時刻只能被一個程序或執行緒使用,多個程序或執行緒不能同時使用公共資源。即就是同一時刻只允許一個執行緒訪問共享資源的問題。
  • 同步(Synchronization):兩個或兩個以上的程序或執行緒在執行過程中協同步調,按預定的先後次序執行。即就是執行緒之間如何通訊、協作的問題。

針對對於這兩大核心問題,利用管程是能夠解決和實現的,因此可以說,管程是併發程式設計的萬能鑰匙。<br />雖然,Java在基於語法層面(synchronized 關鍵字)實現了對管程技術,但是從使用方式和效能上來說,內建鎖(synchronized 關鍵字)的粒度相對過大,不支援超時和中斷等問題。<br />為了彌補這些問題,從JDK層面對其“重複造輪子”,在JDK內部對其重新設計和定義,甚至實現了新的特性。<br />在Java領域中,從JDK原始碼分析來看,基於JDK層面實現的鎖大致主要可以分為以下4種方式:

  • 基於Lock介面實現的鎖:JDK1.5版本提供的ReentrantLock類
  • 基於ReadWriteLock介面實現的鎖:JDK1.5版本提供的ReentrantReadWriteLock類
  • 基於AQS基礎同步器實現的鎖:JDK1.5版本提供的併發相關的同步器Semaphore,CyclicBarrier以及CountDownLatch等�
  • 基於自定義API操作實現的鎖:JDK1.8版本中提供的StampedLock類

從閱讀原始碼不難發現,在Java SDK 併發包主要通過AbstractQueuedSynchronizer(AQS)實現多執行緒同步機制的封裝與定義,而通過Lock 和 Condition 兩個介面來實現管程,其中 Lock 用於解決互斥問題,Condition 用於解決同步問題。

一.AQS基礎同步器基本理論

在Java領域中,同步器是專門為多執行緒併發設計的同步機制,主要是多執行緒併發執行時執行緒之間通過某種共享狀態來實現同步,只有當狀態滿足這種條件時執行緒才往下執行的一種同步機制。

<br />一個標準的AQS同步器主要有同步狀態機制,等待佇列,條件佇列,獨佔模式,共享模式等五大核心要素組成。<br />在Java領域中,JDK的JUC(java.util.concurrent.)包中提供了各種併發工具,但是大部分同步工具的實現基於AbstractQueuedSynchronizer類實現,其內部結構主要如下:

  • 同步狀態機制(Synchronization Status):主要用於實現鎖(Lock)機制,是指同步狀態,其要求對於狀態的更新必須原子性的
  • 等待佇列(Wait Queue):主要用於存放等待執行緒獲取到的鎖資源,並且把執行緒維護到一個Node(節點)裡面和維護一個非阻塞的CHL Node FIFO(先進先出)佇列,主要是採用自旋鎖+CAS操作來保證節點插入和移除的原子性操作。
  • 條件佇列(Condition Queue):用於實現鎖的條件機制,一般主要是指替換“等待-通知”工作機制,主要是通過ConditionObject物件實現Condition介面提供的方法實現。
  • 獨佔模式(Exclusive Mode):主要用於實現獨佔鎖,主要是基於靜態內部類Node的常量標誌EXCLUSIVE來標識該節點是獨佔模式
  • 共享模式(Shared Mode):主要用於實現共享鎖,主要是基於靜態內部類Node的常量標誌SHARED來標識該節點是共享模式

我們可以得到一個比較通用的併發同步工具基礎模型,大致包含如下幾個內容,其中:<br />

  • 條件變數(Conditional Variable): 利用執行緒間共享的變數進行同步的一種工作機制
  • 共享變數((Shared Variable)):一般指物件實體物件的成員變數和屬性
  • 阻塞佇列(Blocking Queue):共享變數(Shared Variable)及其對共享變數的操作統一封裝
  • 等待佇列(Wait Queue):每個條件變數都對應有一個等待佇列(Wait Queue),內部需要實現入隊操作(Enqueue)和出隊操作(Dequeue)方法
  • 變數狀態描述機(Synchronization Status):描述條件變數和共享變數之間狀態變化,又可以稱其為同步狀態
  • 工作模式(Operation Mode): 執行緒資源具有排他性,因此定義獨佔模式和共享模式兩種工作模式

綜上所述,條件變數和等待佇列的作用是解決執行緒之間的同步問題;共享變數與阻塞佇列的作用是解決執行緒之間的互斥問題。

二. JDK顯式鎖統一概念模型

在併發程式設計領域,有兩大核心問題:一個是互斥,即同一時刻只允許一個執行緒訪問共享資源;另一個是同步,即執行緒之間如何通訊、協作。

綜合Java領域中的併發鎖的各種實現與應用分析來看,一把鎖或者一種鎖,基本上都會包含以下幾個方面:

  • 鎖的同步器工作機制:主要是考慮共享模式還是獨享模式,是否支援超時機制,以及是否支援超時機制?
  • 鎖的同步器工作模式:主要是基於AQS基礎同步器封裝內部同步器,是否考慮公平/非公平模式?
  • 鎖的狀態變數機制: 主要鎖的狀態設定,是否共享狀態變數?
  • 鎖的佇列封裝定義:主要是指等待佇列和條件佇列,是否需要條件佇列或者等待佇列定義?
  • 鎖的底層實現操作: 主要是指底層CL鎖和CAS操作,是否需要考慮自旋鎖或者CAS操作例項物件方法?
  • 鎖的組合實現新鎖: 主要是基於獨佔鎖和共享鎖,是否考慮對應API自定義操作實現?

綜上所述,大致可以根據上述這些方向,我們便可以清楚:ideograph_advantage:️知道Java領域中各種鎖實現的基本理論時和實現思想。

三.ReentrantLock(可重入鎖)的設計與實現

在Java領域中,ReentrantLock(可重入鎖)是針對於Java多執行緒併發控制中對一個執行緒可以多次對某個鎖進行加鎖操作,主要是基於內建的AQS基礎抽象佇列同步器實現的一種併發控制工具類。

<br />一般來說,對於同一個執行緒是否可以重複佔有同一個鎖物件的角度來分,大致主要可以分為可重入鎖與不可重入鎖。其中:

  • 可重入鎖:一個執行緒可以多次搶佔同一個鎖,也就意味著能夠支援一個執行緒對資源的重複加鎖,或者說,一個執行緒可以多次進入同一個鎖所同步的臨界區程式碼塊。
  • 不可重入鎖:一個執行緒只能搶佔一次同一個鎖,也就意味著在同一時刻只能有一個執行緒獲取到鎖,而其他獲取鎖的執行緒只能等待,只有擁有鎖的執行緒釋放了鎖後,其他的執行緒才能夠獲取鎖。

ReentrantLock是JDK中顯式鎖一個主要基於Lock介面API實現的基礎實現類,擁有與內建鎖(synchronized)相同的併發性和記憶體語義,同時提供了限時搶佔、可中斷搶佔等一些高階鎖特性。<br />除此之外,ReentrantLock基於內建的AQS基礎抽象佇列同步器實現,線上程參與鎖資源競爭比較激烈的場景下,能表現出比內建鎖較佳的效能。<br />而且,ReentrantLock是一種獨佔鎖,在獨佔模式下只能逐一使用鎖,也就是說,任意時刻最多隻會有一個執行緒持有鎖的控制權。

1. 設計思想

ReentrantLock類最早是在JDK1.5版本提供的,從設計思想上來看,主要包括同步器工作模式,獲取鎖方法,釋放鎖方法以及定義Condition佇列方法等4個核心要素。其中:

  • 實現Lock介面 :主要基於Lock介面API實現對應方法,擁有與內建鎖(synchronized)相同的併發性和記憶體語義,用於支援和解決解決互斥問題。
  • 同步器工作模式:基於AQS基礎抽象佇列同步器封裝內建實現一個靜態的內建同步器抽象類,然後基於這個抽象類分別實現了公平同步器和非公平同步器,用來指定和描述同步器工作模式是公平模式還是非公平模式。
  • 公平/非公平模式:主要描述的是多個執行緒在同時獲取鎖時是否按照先到先得的順序獲取鎖,如果是則為公平模式,否則為非公平模式。
  • 獲取鎖方法:主要定義了一個lock()方法來獲取鎖,表示假如鎖已經被其他執行緒佔有或持有,其當前獲取鎖的執行緒則進入等待狀態。
  • 釋放鎖方法:主要定義了一個unlock()方法來釋放鎖,表示假如鎖已經被其他執行緒放棄或釋放,其當前獲取鎖的執行緒則獲得該鎖。
  • 定義Condition佇列操作方法: 主要是基於Condition介面來定義一個方法實現鎖的條件機制,用於支援執行緒的阻塞和喚醒功能即就是解決同步問題,也就是我們說的執行緒間的通訊方式。
  • 定義等待佇列操作方法: 主要是依據條件佇列來時進行對應的操作,間接適配AQS基礎同步器中對於等待佇列的功能,保證獲取鎖的順序的公平性

2. 基本實現

在ReentrantLock類的JDK1.8版本中,對於ReentrantLock的基本實現如下:

public class ReentrantLock implements Lock, java.io.Serializable {

    private static final long serialVersionUID = 7373984872572414699 L;

    /**
     * ReentrantLock鎖-定義支援同步器實現
     */
    private final Sync sync;

    /**
     * ReentrantLock鎖-基於AQS定義支援同步器實現
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860 L;

        /**
         * ReentrantLock鎖-定義支援同步器Sync獲取鎖方法
         */
        abstract void lock();
				
        //......其他方法程式碼
    }

    /**
     * ReentrantLock鎖-構造同步器預設工作模式(預設非公平模式)
     */
    public ReentrantLock() {
        sync = new NonfairSync();
    }

    /**
     * ReentrantLock鎖-構造同步器指定工作模式(可選公平/非公平模式)
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

    /**
     * ReentrantLock鎖-獲取鎖(普通模式)
     */
    public void lock() {
        sync.lock();
    }

    /**
     * ReentrantLock鎖-釋放鎖
     */
    public void unlock() {
        sync.release(1);
    }

    /**
     * ReentrantLock鎖-建立鎖的條件機制
     */
    public Condition newCondition() {
        return sync.newCondition();
    }

    //......其他方法程式碼
}
  • 內部同步器:基於AQS基礎同步器封裝和定義了一個靜態內部Sync抽象類,其中抽象了一個內建鎖lock()方法
  • 同步器工作模式:提供了 2個構造方法,其中無引數構造方法表示的是預設的工作模式,有引數構造方法主要依據引數來實現指定的工作模式
  • 獲取鎖: 主要是提供了lock()方法,呼叫的靜態內部Sync抽象類內建鎖lock()方法,而本質上是AQS同步器中的acquire()方法
  • 釋放鎖: 主要是提供了unlock()方法,而本質上是呼叫的AQS同步器中的release()方法
  • 建立條件佇列: 主要是基於Condition介面定義了newCondition() 方法,呼叫的靜態內部Sync抽象類ewCondition()方法,而本質上是呼叫的AQS同步器中的ConditionObject中的newCondition()方法

2.1 基於AQS同步器封裝靜態內部Sync抽象類

abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        /**
         * ReentrantLock鎖-內部同步器Sync的內建加鎖方法
         */
        abstract void lock();

        /**
         * ReentrantLock鎖-內部同步器Sync的非公平獲取鎖
         */
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

        /**
         * ReentrantLock鎖-內部同步器Sync的嘗試釋放
         */
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }


        /**
         * ReentrantLock鎖-內部同步器Sync的檢查執行緒是否獨佔
         */
        protected final boolean isHeldExclusively() {
            // While we must in general read state before owner,
            // we don't need to do so to check if current thread is owner
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

        /**
         * ReentrantLock鎖-內部同步器Sync的條件機制
         */
        final ConditionObject newCondition() {
            return new ConditionObject();
        }


        /**
         * ReentrantLock鎖-內部同步器Sync的判斷鎖持有者
         */
        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }

        /**
         * ReentrantLock鎖-內部同步器Sync的獨佔狀態
         */
        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }

        /**
         * ReentrantLock鎖-內部同步器Sync的是否被鎖
         */
        final boolean isLocked() {
            return getState() != 0;
        }

        /**
         * ReentrantLock鎖-內部同步器Sync的流化處理物件
         */
        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
				
    }
  • Sync類:FairSync公平同步器和NonfairSync非公平同步器的抽象父類。
  • 校驗isHeldExclusively()方法: ReentrantLock鎖是屬於獨佔模式,需要當前鎖持有執行緒與當前執行緒是否一致
  • nonfairTryAcquire()方法: 一般主要用於非公平模式獲取鎖,其核心是compareAndSetState方法和setExclusiveOwnerThread方法
  • tryRelease()方法: 其公平/非公平模式都是通過ryRelease()來釋放鎖操作
  • newCondition()方法: 基於AQS同步器的ConditionObject物件封裝實現,提供給 ReentrantLock類使用
  • 私有readObject()方法:對於輸入的物件進行流化處理

特別需要注意的是,我們需要重點關注nonfairTryAcquire()方法和tryRelease()方法,其中:

  • 獲取非公平鎖 nonfairTryAcquire()方法:主要是用於獲取AQS的狀態變數status,其預設取值範圍是0和1,其中,0表示未被加鎖,1表示已經被加鎖
    • 如果狀態變數status=0,使用compareAndSetState方法進行CAS原子修改操作,把狀態變數修改為1,並且通過setExclusiveOwnerThread設定當前執行緒為鎖的持有執行緒
    • 如果狀態變數status=1,表示當前執行緒為鎖的持有執行緒,正在進入鎖重入操作,狀態變數累加1,超過重入次數時,會丟擲throw new Error("Maximum lock count exceeded")
  • 釋放鎖tryRelease()方法:主要是檢查當前執行緒是否為鎖持有執行緒,隨後AQS同步器狀態變數減1,如果不是 throw new IllegalMonitorStateException()
    • 如果狀態變數status=0,表示鎖已經釋放成功,通過setExclusiveOwnerThread設定鎖的持有執行緒為null,也就是置空鎖的持有執行緒
    • 如果狀態變數status !=0,  需要狀態變數遞減1即可,直到鎖已經釋放成功

2.2 基於Sync抽象類封裝FairSync公平同步器

static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        /**
         * ReentrantLock鎖-公平模式-獲取鎖
         */
        final void lock() {
            acquire(1);
        }

        /**
         * ReentrantLock鎖-公平模式-嘗試獲取鎖
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                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;
        }
    }
  • 實現方式: 主要基於AQS封裝的內部靜態抽象Sync同步類實現,使用的AQS的獨佔模式。
  • 主要方法: 主要提供了lock()和tryAcquire()方法,其嚴格意義上來說,僅僅只是實現了tryAcquire()方法,但是最關鍵的使用hasQueuedPredecessors來保證了鎖的公平性。
  • 鎖獲取方式: 主要是採用完全通過佇列來實現實現公平機制,即就是檢查是否存在等待佇列,如果佇列之中已經存在其他執行緒,直接放棄操作。

2.3 基於Sync抽象類封裝NonfairSync非公平同步器

static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * ReentrantLock鎖-非公平模式-獲取鎖
         */
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        /**
         * ReentrantLock鎖-非公平模式-嘗試獲取鎖
         */
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }
  • 實現方式: 主要基於AQS封裝的內部靜態抽象Sync同步類實現,使用的AQS的獨佔模式。
  • 主要方法: 主要提供了lock()和tryAcquire()方法,其嚴格意義上來說,僅僅只是實現了tryAcquire()方法,直接呼叫了Sync同步類的nonfairTryAcquire()方法。
  • 鎖獲取方式: 主要是採用闖入策略來打破鎖的公平,也就是一般準備獲取鎖的執行緒會先嚐試獲取鎖,失敗之後才進入佇列中。

3. 具體實現

在ReentrantLock類的JDK1.8版本中,對於ReentrantLock的具體實現如下:

public class ReentrantLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = 7373984872572414699 L;
    /** Synchronizer providing all implementation mechanics */
    private final Sync sync;

    /**
     * ReentrantLock鎖-基於AQS定義支援同步器實現
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860 L;

        /**
         * ReentrantLock鎖-定義支援同步器Sync獲取鎖方法
         */
        abstract void lock();
				
        //......其他方法程式碼
    }

    /**
     * ReentrantLock鎖-構造同步器預設工作模式(預設非公平模式)
     */
    public ReentrantLock() {
        sync = new NonfairSync();
    }

    /**
     * ReentrantLock鎖-構造同步器指定工作模式(可選公平/非公平模式)
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

    /**
     * ReentrantLock鎖-獲取鎖(普通模式)
     */
    public void lock() {
        sync.lock();
    }

    /**
     * ReentrantLock鎖-釋放鎖
     */
    public void unlock() {
        sync.release(1);
    }

    /**
     * ReentrantLock鎖-建立鎖的條件機制
     */
    public Condition newCondition() {
        return sync.newCondition();
    }
		
    /**
     * ReentrantLock鎖-獲取鎖(支援可中斷機制)
     */
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    /**
     * ReentrantLock鎖-嘗試獲取鎖(普通模式)
     */
    public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }

    /**
     * ReentrantLock鎖-嘗試獲取鎖(支援超時)
     */
    public boolean tryLock(long timeout, TimeUnit unit)
    throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }

    /**
     * ReentrantLock鎖-統計當前執行緒所持有數量
     */
    public int getHoldCount() {
        return sync.getHoldCount();
    }


    /**
     * ReentrantLock鎖-檢測當前執行緒是否獨佔
     */
    public boolean isHeldByCurrentThread() {
        return sync.isHeldExclusively();
    }

    /**
     * ReentrantLock鎖-檢測是否被加鎖
     */
    public boolean isLocked() {
        return sync.isLocked();
    }

    /**
     * ReentrantLock鎖-檢測是否公平模式
     */
    public final boolean isFair() {
        return sync instanceof FairSync;
    }

    /**
     * ReentrantLock鎖-獲取當前鎖持有執行緒
     */
    protected Thread getOwner() {
        return sync.getOwner();
    }

    /**
     * ReentrantLock鎖-檢測輪詢執行緒是否存在佇列中
     */
    public final boolean hasQueuedThreads() {
        return sync.hasQueuedThreads();
    }

    /**
     * ReentrantLock鎖-檢測執行緒是否存在佇列中
     */
    public final boolean hasQueuedThread(Thread thread) {
        return sync.isQueued(thread);
    }

    /**
     * ReentrantLock鎖-獲取佇列數量
     */
    public final int getQueueLength() {
        return sync.getQueueLength();
    }

    /**
     * ReentrantLock鎖-獲取佇列中的所有執行緒
     */
    protected Collection < Thread > getQueuedThreads() {
        return sync.getQueuedThreads();
    }

    /**
     * ReentrantLock鎖-檢測存在條件佇列是否入隊狀態
     */
    public boolean hasWaiters(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject) condition);
    }

    /**
     * ReentrantLock鎖-獲取等待佇列的長度
     */
    public int getWaitQueueLength(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject) condition);
    }

    /**
     * ReentrantLock鎖-獲取等待佇列的執行緒物件
     */
    protected Collection < Thread > getWaitingThreads(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject) condition);
    }

}
  • 獲取鎖的方法:主要提供了一般模式lock()方法,支援可中斷機制lockInterruptibly()方法,無引數tryLock()方法以及有引數的支援超時機制的tryLock(long timeout, TimeUnit unit)方法
  • 釋放鎖的方法:主要是unlock()方法,間接呼叫是內部同步器中的release()方法
  • 條件佇列操作:主要提供了獲取佇列中的執行緒物件getQueuedThreads(),檢測佇列入隊hasWaiters(Condition condition) 方法,以及
  • 等待佇列操作:主要提供了獲取佇列中的執行緒物件getWaitingThreads(Condition condition),檢測佇列入隊hasQueuedThread(Thread thread) 方法,以及獲取佇列長度getQueueLength()方法和getWaitingThreads(Condition condition)方法
  • 其他檢測判斷:主要有判斷是否公平模式isFair()方法,是否當前執行緒獨佔isHeldByCurrentThread()方法,以及是否加鎖 isLocked()等

需要注意的是,在JDK1.8版本之後,對於ReentrantLock的實現有些細微的變化,感興趣的可自行參考相關版本的原始碼進行對比分析。

綜上所述,從一定意義上講,ReentrantLock是一種可重入的獨佔(互斥)鎖,屬於AQS基礎抽象佇列同步器中獨佔模式孵化的產物,支援公平模式與非公平模式,預設採用非公平模式。

寫在最後

通過對Java領域中,JDK內部提供的各種鎖的實現來看,一直圍繞的核心主要還是基於AQS基礎同步器來實現的,但是AQS基礎同步器不是一種非它不可的技術標準規範,更多的只是一套技術參考指南。

但是,實際上,Java對於鎖的實現與運用遠遠不止這些,還有相位器(Phaser)和交換器(Exchanger),以及在Java JDK1.8版本之前併發容器ConcurrentHashMap中使用的分段鎖(Segment)。

不論是何種實現和應用,在Java併發程式設計領域來講,都是圍繞執行緒安全問題的角度去考慮的,只是針對於各種各樣的業務場景做的具體的實現。

一定意義上來講,對執行緒加鎖只是併發程式設計的實現方式之一,相對於實際應用來說,Java領域中的鎖都只是一種單一應用的鎖,只是給我們掌握Java併發程式設計提供一種思想沒,三言兩語也不可能詳盡。

到此為止,這算是對於Java領域中併發鎖的最終章,文中表述均為個人看法和個人理解,如有不到之處,忘請諒解也請給予批評指正。

最後,技術研究之路任重而道遠,願我們熬的每一個通宵,都撐得起我們想在這條路上走下去的勇氣,未來仍然可期,與各位程式程式設計君共勉!

版權宣告:本文為博主原創文章,遵循相關版權協議,如若轉載或者分享請附上原文出處連結和連結來源。