蒼穹之邊,浩瀚之摯,眰恦之美; 悟心悟性,善始善終,惟善惟道! —— 朝槿《朝槿兮年說》
寫在開頭
在併發編程領域,有兩大核心問題:一個是互斥,即同一時刻只允許一個線程訪問共享資源;另一個是同步,即線程之間如何通信、協作。
主要原因是,對於多線程實現實現併發,一直以來,多線程都存在2個問題:
- 線程之間內存共享,需要通過加鎖進行控制,但是加鎖會導致性能下降,同時複雜的加鎖機制也會增加編程編碼難度
- 過多線程造成線程之間的上下文切換,導致效率低下
因此,在併發編程領域中,一直有一個很重要的設計原則: “ 不要通過內存共享來實現通信,而應該通過通信來實現內存共享。”
簡單來說,就是儘可能通過消息通信,而不是內存共享來實現進程或者線程之間的同步。
關健術語
本文用到的一些關鍵詞語以及常用術語,主要如下:
- 併發(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):兩個或兩個以上的進程或線程在運行過程中協同步調,按預定的先後次序運行。即就是線程之間如何通信、協作的問題。
針對對於這兩大核心問題,利用管程是能夠解決和實現的,因此可以說,管程是併發編程的萬能鑰匙。
雖然,Java在基於語法層面(synchronized 關鍵字)實現了對管程技術,但是從使用方式和性能上來說,內置鎖(synchronized 關鍵字)的粒度相對過大,不支持超時和中斷等問題。
爲了彌補這些問題,從JDK層面對其“重複造輪子”,在JDK內部對其重新設計和定義,甚至實現了新的特性。
在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領域中,同步器是專門爲多線程併發設計的同步機制,主要是多線程併發執行時線程之間通過某種共享狀態來實現同步,只有當狀態滿足這種條件時線程才往下執行的一種同步機制。
一個標準的AQS同步器主要有同步狀態機制,等待隊列,條件隊列,獨佔模式,共享模式等五大核心要素組成。
在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來標識該節點是共享模式
我們可以得到一個比較通用的併發同步工具基礎模型,大致包含如下幾個內容,其中:
- 條件變量(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自定義操作實現?
綜上所述,大致可以根據上述這些方向,我們便可以清楚🉐️知道Java領域中各種鎖實現的基本理論時和實現思想。
五.StampedLock(印戳鎖)的設計與實現
在Java領域中,StampedLock(印戳鎖)是針對於Java多線程併發控制中引入一個共享鎖定義讀操作與獨佔鎖定義讀操作等場景共同組合構成一把鎖來提高併發,主要是基於自定義API操作實現的一種併發控制工具類。
1. 設計思想
StampedLock(印戳鎖)是對ReentrantReadWriteLock讀寫鎖的一 種改進,主要的改進爲:在沒有寫只有讀的場景下,StampedLock支持 不用加讀鎖而是直接進行讀操作,最大程度提升讀的效率,只有在發 生過寫操作之後,再加讀鎖才能進行讀操作。
一般來說,StampedLock 裏的寫鎖和悲觀讀鎖加鎖成功之後,都會返回一個 stamp;然後解鎖的時候,需要傳入這個 stamp。
1.1 印戳鎖的基本理論
雖然基於AQS基礎同步器實現了各種鎖,但是由於採用的自旋鎖+CAS操作方式會導致如下兩個問題:
- CAS惡性空自旋會浪費大量的CPU資源
- 在SMP架構的CPU上會導致“總線風暴”問題
解決CAS惡性空自旋的有效方式之一是以空間換時間,較爲常見的 方案有兩種:分散操作熱點和使用隊列削峯。
基於這個基礎,在JDK1.8版本中,基於使用隊列削峯的方式,自定義API操作,提供了StampedLock(印戳鎖)的實現。
簡單來說,StampedLock(印戳鎖)提供了三種鎖的實現模式,其中:
- 悲觀讀鎖:與ReadWriteLock的讀鎖類似,多個線程可以同 時獲取悲觀讀鎖,悲觀讀鎖是一個共享鎖。
- 樂觀讀鎖:相當於直接操作數據,不加任何鎖,連讀鎖都不 要。
- 寫鎖:與ReadWriteLock的寫鎖類似,寫鎖和悲觀讀鎖是互 斥的。雖然寫鎖與樂觀讀鎖不會互斥,但是在數據被更新之後,之前 通過樂觀讀鎖獲得的數據已經變成了髒數據。
1.1 印戳鎖的實現思想
StampedLock(印戳鎖)與其他顯式鎖不同的是,主要是是最早在JDK1.8版本中提供的,從設計思想上來看,主要包括共享狀態變量機制,內置的等待數據隊列,讀鎖視圖,寫鎖視圖以及讀寫鎖視圖等5個核心要素。其中:
- 共享狀態變量機制:主要是在內部封裝一些靜態私有的常量,用於描述各個模式之間的狀態描述等。
- 內置的等待數據隊列:主要是自定義實現一個基於CLH鎖的等待隊列
- 讀鎖視圖:基於Lock接口實現一個對應讀鎖的視圖
- 寫鎖視圖:基於Lock接口實現一個對應寫鎖的視圖
- 讀寫鎖視圖:基於ReadWriteLock接口實現一個包含讀鎖和寫鎖的視圖
2. 基本實現
在StampedLock(印戳鎖)類的JDK1.8版本中,對於StampedLock的基本實現如下:
/** StampedLock鎖-最早在JDK1.8中實現的 */
public class StampedLock implements java.io.Serializable {
private static final long serialVersionUID = -6001602636862214147L;
/** StampedLock鎖-自旋控制的最大允許核心線程數 */
private static final int NCPU = Runtime.getRuntime().availableProcessors();
/** StampedLock鎖-等待隊列自旋控制的最大自旋閾值 */
private static final int SPINS = (NCPU > 1) ? 1 << 6 : 0;
/** StampedLock鎖-等待隊列頭節點自旋控制的最大自旋閾值 */
private static final int HEAD_SPINS = (NCPU > 1) ? 1 << 10 : 0;
/** StampedLock鎖-等待隊列頭節點自旋控制的最大自旋閾值 */
private static final int MAX_HEAD_SPINS = (NCPU > 1) ? 1 << 16 : 0;
/** StampedLock鎖-進入阻塞之前的最大重試次數 */
private static final int OVERFLOW_YIELD_RATE = 7; // must be power 2 - 1
//... 其他鎖資源的狀態常量
/** StampedLock鎖-CLH隊列的頭部(head)節點 */
private transient volatile WNode whead;
/** StampedLock鎖-CLH隊列的尾部(tail)節點 */
private transient volatile WNode wtail;
/** StampedLock鎖-讀鎖視圖*/
transient ReadLockView readLockView;
/** StampedLock鎖-寫鎖視圖*/
transient WriteLockView writeLockView;
/** StampedLock鎖-讀寫鎖視圖 */
transient ReadWriteLockView readWriteLockView;
/** StampedLock鎖-鎖的最原始的狀態初始值 */
private static final long ORIGIN = WBIT << 1;
/** StampedLock鎖-各種鎖的同步狀態變量 */
private transient volatile long state;
/** StampedLock鎖-讀鎖的溢出的拓展標記 */
private transient int readerOverflow;
/** StampedLock鎖-構造方法 */
public StampedLock() {
state = ORIGIN;
}
/** StampedLock鎖-實例化ReadLock方法 */
public Lock asReadLock() {
ReadLockView v;
return ((v = readLockView) != null ? v :
(readLockView = new ReadLockView()));
}
/** StampedLock鎖-實例化WriteLock方法 */
public Lock asWriteLock() {
WriteLockView v;
return ((v = writeLockView) != null ? v :
(writeLockView = new WriteLockView()));
}
/** StampedLock鎖-實例化ReadWriteLock方法 */
public ReadWriteLock asReadWriteLock() {
ReadWriteLockView v;
return ((v = readWriteLockView) != null ? v :
(readWriteLockView = new ReadWriteLockView()));
}
/** StampedLock鎖-獲取ReadLock方法 */
public long readLock() {
long s = state, next; // bypass acquireRead on common uncontended case
return ((whead == wtail && (s & ABITS) < RFULL &&
U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) ?
next : acquireRead(false, 0L));
}
/** StampedLock鎖-獲取WriteLock方法 */
public long writeLock() {
long s, next; // bypass acquireWrite in fully unlocked case only
return ((((s = state) & ABITS) == 0L &&
U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
next : acquireWrite(false, 0L));
}
//... 其他代碼
}
2.1 共享狀態變量機制
/** StampedLock鎖-最早在JDK1.8中實現的 */
public class StampedLock implements java.io.Serializable {
/** StampedLock鎖-自旋控制的最大允許核心線程數 */
private static final int NCPU = Runtime.getRuntime().availableProcessors();
/** StampedLock鎖-等待隊列自旋控制的最大自旋閾值 */
private static final int SPINS = (NCPU > 1) ? 1 << 6 : 0;
/** StampedLock鎖-等待隊列頭節點自旋控制的最大自旋閾值 */
private static final int HEAD_SPINS = (NCPU > 1) ? 1 << 10 : 0;
/** StampedLock鎖-等待隊列頭節點自旋控制的最大自旋閾值 */
private static final int MAX_HEAD_SPINS = (NCPU > 1) ? 1 << 16 : 0;
/** StampedLock鎖-進入阻塞之前的最大重試次數 */
private static final int OVERFLOW_YIELD_RATE = 7; // must be power 2 - 1
// Values for lock state and stamp operations
/** StampedLock鎖-讀鎖移動的位數 */
private static final long RUNIT = 1L;
/** StampedLock鎖-寫鎖移動的位數 */
private static final long WBIT = 1L << LG_READERS;
/** StampedLock鎖-讀鎖移動的位數 */
private static final long RBITS = WBIT - 1L;
/** StampedLock鎖-讀鎖移動的位數 */
private static final long RFULL = RBITS - 1L;
/** StampedLock鎖-讀寫鎖移動的位數 */
private static final long ABITS = RBITS | WBIT;
/** StampedLock鎖-讀寫鎖移動的位數 */
private static final long SBITS = ~RBITS;
// Special value from cancelled acquire methods so caller can throw IE
/** StampedLock鎖-線程對象中斷標識 */
private static final long INTERRUPTED = 1L;
// Values for node status; order matters
/** StampedLock鎖-最早在JDK1.8中實現的 */
private static final int WAITING = -1;
/** StampedLock鎖-最早在JDK1.8中實現的 */
private static final int CANCELLED = 1;
// Modes for nodes (int not boolean to allow arithmetic)
/** StampedLock鎖-用於表示在隊列之中是讀模式 */
private static final int RMODE = 0;
/** StampedLock鎖-用於表示在隊列之中是寫模式 */
private static final int WMODE = 1;
//... 其他代碼
// Unsafe mechanics
/** StampedLock鎖-實例化Unsafe對象 */
private static final sun.misc.Unsafe U;
/** StampedLock鎖-狀態 */
private static final long STATE;
/** StampedLock鎖-頭部節點 */
private static final long WHEAD;
/** StampedLock鎖-尾部節點 */
private static final long WTAIL;
/** StampedLock鎖-後繼節點 */
private static final long WNEXT;
/** StampedLock鎖-節點狀態 */
private static final long WSTATUS;
/** StampedLock鎖-節點鏈表 */
private static final long WCOWAIT;
/** StampedLock鎖-中斷標識 */
private static final long PARKBLOCKER;
static {
try {
U = sun.misc.Unsafe.getUnsafe();
Class<?> k = StampedLock.class;
Class<?> wk = WNode.class;
STATE = U.objectFieldOffset
(k.getDeclaredField("state"));
WHEAD = U.objectFieldOffset
(k.getDeclaredField("whead"));
WTAIL = U.objectFieldOffset
(k.getDeclaredField("wtail"));
WSTATUS = U.objectFieldOffset
(wk.getDeclaredField("status"));
WNEXT = U.objectFieldOffset
(wk.getDeclaredField("next"));
WCOWAIT = U.objectFieldOffset
(wk.getDeclaredField("cowait"));
Class<?> tk = Thread.class;
PARKBLOCKER = U.objectFieldOffset
(tk.getDeclaredField("parkBlocker"));
} catch (Exception e) {
throw new Error(e);
}
}
}
對於StampedLock鎖中對於各種資源的標記,其封裝了一系列的常量,主要可以分爲以下幾個方面,其中:
- 核心資源常量標識:是對線程操作資源的提供的常量封裝,其中:
- NCPU:自旋控制的核心線程數量,主要通過Runtime.getRuntime().availableProcessors()獲取設置。
- SPINS:等待隊列自旋控制的最大自旋閾值,主要通過 (_NCPU _> 1) ? 1 << 6 : 0獲取設置
- HEAD_SPINS: 等待隊列頭節點自旋控制的自旋閾值,主要通過 (_NCPU _> 1) ? 1 << 10 : 0獲取設置
- MAX_HEAD_SPINS:等待隊列頭節點自旋控制的最大自旋閾值,主要通過(_NCPU _> 1) ? 1 << 16 : 0獲取設置
- OVERFLOW_YIELD_RATE:線程讓步操作等待的自旋閾值,默認值爲7
- LG_READERS:讀鎖溢出的最大閾值,默認值爲7
- _INTERRUPTED:線程中斷標識,_默認值爲1L
- 鎖狀態值設置標識:
- ORIGIN:鎖狀態的初始值,默認值爲WBIT << 1,如果分配失敗默認設置爲0
- 鎖狀態的操作標識:
- RUNIT:讀鎖移動的位數,默認值爲 1
- WBIT:寫鎖移動的位數,默認值爲1L << LG_READERS
- RBITS:讀鎖移動的位數_,_默認值爲_WBIT _- 1L
- RFULL:移動的位數,默認值爲_RBITS _- 1L
- ABITS:鎖移動的位數,默認值爲 _RBITS _| WBIT
- SBITS:鎖移動的位數,默認值爲 ~RBITS
- 等待隊列節點標識:
- WAITING:等待狀態的初始值,默認值爲-1
- CANCELLED:取消狀態的初始值,默認值爲1
- 讀寫鎖的模式標識:
- RMODE:讀鎖模式,默認值爲0
- WMODE:寫鎖模式,默認值爲1
- CAS操作狀態標識:封裝了CAS操作狀態標識,還通過反射實例化了Unsafe對象實例。
2.2 內置的等待隊列WNode
/** StampedLock鎖-最早在JDK1.8中實現的 */
public class StampedLock implements java.io.Serializable {
/** Wait nodes */
static final class WNode {
/** StampedLock鎖-隊列前驅節點 */
volatile WNode prev;
/** StampedLock鎖-隊列後驅節點 */
volatile WNode next;
/** StampedLock鎖-鎖的存儲列表 */
volatile WNode cowait; // list of linked readers
/** StampedLock鎖-線程對象 */
volatile Thread thread; // non-null while possibly parked
/** StampedLock鎖-鎖的狀態 */
volatile int status; // 0, WAITING, or CANCELLED
/** StampedLock鎖-鎖的模式 */
final int mode; // RMODE or WMODE
WNode(int m, WNode p) { mode = m; prev = p; }
}
/** Head of CLH queue */
/** StampedLock鎖-頭部節點 */
private transient volatile WNode whead;
/** Tail (last) of CLH queue */
/** StampedLock鎖-尾部節點 */
private transient volatile WNode wtail;
}
對於StampedLock鎖對於等待隊列的實現,主要包含以下幾個方面的內容,其中:
- 封裝了一個等待隊列WNode的靜態內部類,其中:
- prev:等待隊列的前驅節點
- next:等待隊列的後驅節點
- cowait:表示依據鎖標記存儲當前線程入隊的情況,隊列鎖列表
- thread: 線程對象,一般都是當前獲取鎖的線程
- status:用於表示鎖的狀態變量,對應着常量0,WAITING(-1), CANCELLED(1),其中,0表示正常狀態,WAITING(-1)爲等待狀態,CANCELLED(1)爲取消狀態。
- mode:用於表示鎖的模式,對應着常量RMODE和WMODE,其中RMODE爲寫模式,WMOD爲讀模式
- 構造方法WNode(int m, WNode p):用於實例化WNode對象,實現一個等待隊列
- 實例化等待隊列對象,主要封裝一個頭部節點whead和尾部節點wtail的對象
2.3 共用的讀鎖核心處理邏輯
首先,對於StampedLock鎖的讀鎖視圖與寫鎖視圖的隊列操作,有一個核心的處理邏輯:
/** StampedLock鎖-最早在JDK1.8中實現的 */
public class StampedLock implements java.io.Serializable {
/** StampedLock鎖-取消入隊列 */
private long cancelWaiter(WNode node, WNode group, boolean interrupted) {
if (node != null && group != null) {
Thread w;
node.status = CANCELLED;
// unsplice cancelled nodes from group
for (WNode p = group, q; (q = p.cowait) != null;) {
if (q.status == CANCELLED) {
U.compareAndSwapObject(p, WCOWAIT, q, q.cowait);
p = group; // restart
}
else
p = q;
}
if (group == node) {
for (WNode r = group.cowait; r != null; r = r.cowait) {
if ((w = r.thread) != null)
U.unpark(w); // wake up uncancelled co-waiters
}
for (WNode pred = node.prev; pred != null; ) { // unsplice
WNode succ, pp; // find valid successor
while ((succ = node.next) == null ||
succ.status == CANCELLED) {
WNode q = null; // find successor the slow way
for (WNode t = wtail; t != null && t != node; t = t.prev)
if (t.status != CANCELLED)
q = t; // don't link if succ cancelled
if (succ == q || // ensure accurate successor
U.compareAndSwapObject(node, WNEXT,
succ, succ = q)) {
if (succ == null && node == wtail)
U.compareAndSwapObject(this, WTAIL, node, pred);
break;
}
}
if (pred.next == node) // unsplice pred link
U.compareAndSwapObject(pred, WNEXT, node, succ);
if (succ != null && (w = succ.thread) != null) {
succ.thread = null;
U.unpark(w); // wake up succ to observe new pred
}
if (pred.status != CANCELLED || (pp = pred.prev) == null)
break;
node.prev = pp; // repeat if new pred wrong/cancelled
U.compareAndSwapObject(pp, WNEXT, pred, succ);
pred = pp;
}
}
}
WNode h; // Possibly release first waiter
while ((h = whead) != null) {
long s; WNode q; // similar to release() but check eligibility
if ((q = h.next) == null || q.status == CANCELLED) {
for (WNode t = wtail; t != null && t != h; t = t.prev)
if (t.status <= 0)
q = t;
}
if (h == whead) {
if (q != null && h.status == 0 &&
((s = state) & ABITS) != WBIT && // waiter is eligible
(s == 0L || q.mode == RMODE))
release(h);
break;
}
}
return (interrupted || Thread.interrupted()) ? INTERRUPTED : 0L;
}
}
其次,對於StampedLock鎖的讀鎖視圖的實現作來看,主要核心處理如下:
/** StampedLock鎖-最早在JDK1.8中實現的 */
public class StampedLock implements java.io.Serializable {
/** StampedLock鎖-獲取讀鎖 */
private long acquireRead(boolean interruptible, long deadline) {
WNode node = null, p;
for (int spins = -1;;) {
WNode h;
if ((h = whead) == (p = wtail)) {
for (long m, s, ns;;) {
if ((m = (s = state) & ABITS) < RFULL ?
U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
(m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L))
return ns;
else if (m >= WBIT) {
if (spins > 0) {
if (LockSupport.nextSecondarySeed() >= 0)
--spins;
}
else {
if (spins == 0) {
WNode nh = whead, np = wtail;
if ((nh == h && np == p) || (h = nh) != (p = np))
break;
}
spins = SPINS;
}
}
}
}
if (p == null) { // initialize queue
WNode hd = new WNode(WMODE, null);
if (U.compareAndSwapObject(this, WHEAD, null, hd))
wtail = hd;
}
else if (node == null)
node = new WNode(RMODE, p);
else if (h == p || p.mode != RMODE) {
if (node.prev != p)
node.prev = p;
else if (U.compareAndSwapObject(this, WTAIL, p, node)) {
p.next = node;
break;
}
}
else if (!U.compareAndSwapObject(p, WCOWAIT,
node.cowait = p.cowait, node))
node.cowait = null;
else {
for (;;) {
WNode pp, c; Thread w;
if ((h = whead) != null && (c = h.cowait) != null &&
U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
(w = c.thread) != null) // help release
U.unpark(w);
if (h == (pp = p.prev) || h == p || pp == null) {
long m, s, ns;
do {
if ((m = (s = state) & ABITS) < RFULL ?
U.compareAndSwapLong(this, STATE, s,
ns = s + RUNIT) :
(m < WBIT &&
(ns = tryIncReaderOverflow(s)) != 0L))
return ns;
} while (m < WBIT);
}
if (whead == h && p.prev == pp) {
long time;
if (pp == null || h == p || p.status > 0) {
node = null; // throw away
break;
}
if (deadline == 0L)
time = 0L;
else if ((time = deadline - System.nanoTime()) <= 0L)
return cancelWaiter(node, p, false);
Thread wt = Thread.currentThread();
U.putObject(wt, PARKBLOCKER, this);
node.thread = wt;
if ((h != pp || (state & ABITS) == WBIT) &&
whead == h && p.prev == pp)
U.park(false, time);
node.thread = null;
U.putObject(wt, PARKBLOCKER, null);
if (interruptible && Thread.interrupted())
return cancelWaiter(node, p, true);
}
}
}
}
for (int spins = -1;;) {
WNode h, np, pp; int ps;
if ((h = whead) == p) {
if (spins < 0)
spins = HEAD_SPINS;
else if (spins < MAX_HEAD_SPINS)
spins <<= 1;
for (int k = spins;;) { // spin at head
long m, s, ns;
if ((m = (s = state) & ABITS) < RFULL ?
U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
(m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) {
WNode c; Thread w;
whead = node;
node.prev = null;
while ((c = node.cowait) != null) {
if (U.compareAndSwapObject(node, WCOWAIT,
c, c.cowait) &&
(w = c.thread) != null)
U.unpark(w);
}
return ns;
}
else if (m >= WBIT &&
LockSupport.nextSecondarySeed() >= 0 && --k <= 0)
break;
}
}
else if (h != null) {
WNode c; Thread w;
while ((c = h.cowait) != null) {
if (U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
(w = c.thread) != null)
U.unpark(w);
}
}
if (whead == h) {
if ((np = node.prev) != p) {
if (np != null)
(p = np).next = node; // stale
}
else if ((ps = p.status) == 0)
U.compareAndSwapInt(p, WSTATUS, 0, WAITING);
else if (ps == CANCELLED) {
if ((pp = p.prev) != null) {
node.prev = pp;
pp.next = node;
}
}
else {
long time;
if (deadline == 0L)
time = 0L;
else if ((time = deadline - System.nanoTime()) <= 0L)
return cancelWaiter(node, node, false);
Thread wt = Thread.currentThread();
U.putObject(wt, PARKBLOCKER, this);
node.thread = wt;
if (p.status < 0 &&
(p != h || (state & ABITS) == WBIT) &&
whead == h && node.prev == p)
U.park(false, time);
node.thread = null;
U.putObject(wt, PARKBLOCKER, null);
if (interruptible && Thread.interrupted())
return cancelWaiter(node, node, true);
}
}
}
}
}
然後,對於StampedLock鎖的寫鎖視圖的實現作來看,主要核心處理如下:
/** StampedLock鎖-最早在JDK1.8中實現的 */
public class StampedLock implements java.io.Serializable {
/** StampedLock鎖-獲取寫鎖 */
private long acquireWrite(boolean interruptible, long deadline) {
WNode node = null, p;
for (int spins = -1;;) { // spin while enqueuing
long m, s, ns;
if ((m = (s = state) & ABITS) == 0L) {
if (U.compareAndSwapLong(this, STATE, s, ns = s + WBIT))
return ns;
}
else if (spins < 0)
spins = (m == WBIT && wtail == whead) ? SPINS : 0;
else if (spins > 0) {
if (LockSupport.nextSecondarySeed() >= 0)
--spins;
}
else if ((p = wtail) == null) { // initialize queue
WNode hd = new WNode(WMODE, null);
if (U.compareAndSwapObject(this, WHEAD, null, hd))
wtail = hd;
}
else if (node == null)
node = new WNode(WMODE, p);
else if (node.prev != p)
node.prev = p;
else if (U.compareAndSwapObject(this, WTAIL, p, node)) {
p.next = node;
break;
}
}
for (int spins = -1;;) {
WNode h, np, pp; int ps;
if ((h = whead) == p) {
if (spins < 0)
spins = HEAD_SPINS;
else if (spins < MAX_HEAD_SPINS)
spins <<= 1;
for (int k = spins;;) { // spin at head
long s, ns;
if (((s = state) & ABITS) == 0L) {
if (U.compareAndSwapLong(this, STATE, s,
ns = s + WBIT)) {
whead = node;
node.prev = null;
return ns;
}
}
else if (LockSupport.nextSecondarySeed() >= 0 &&
--k <= 0)
break;
}
}
else if (h != null) { // help release stale waiters
WNode c; Thread w;
while ((c = h.cowait) != null) {
if (U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
(w = c.thread) != null)
U.unpark(w);
}
}
if (whead == h) {
if ((np = node.prev) != p) {
if (np != null)
(p = np).next = node; // stale
}
else if ((ps = p.status) == 0)
U.compareAndSwapInt(p, WSTATUS, 0, WAITING);
else if (ps == CANCELLED) {
if ((pp = p.prev) != null) {
node.prev = pp;
pp.next = node;
}
}
else {
long time; // 0 argument to park means no timeout
if (deadline == 0L)
time = 0L;
else if ((time = deadline - System.nanoTime()) <= 0L)
return cancelWaiter(node, node, false);
Thread wt = Thread.currentThread();
U.putObject(wt, PARKBLOCKER, this);
node.thread = wt;
if (p.status < 0 && (p != h || (state & ABITS) != 0L) &&
whead == h && node.prev == p)
U.park(false, time); // emulate LockSupport.park
node.thread = null;
U.putObject(wt, PARKBLOCKER, null);
if (interruptible && Thread.interrupted())
return cancelWaiter(node, node, true);
}
}
}
}
}
最後,綜合對於StampedLock鎖的讀鎖和寫鎖的獲取和釋放等操作來看,主要核心處理都會調用以下2個方法,其中:
/** StampedLock鎖-最早在JDK1.8中實現的 */
public class StampedLock implements java.io.Serializable {
/** StampedLock鎖-讀鎖溢出遞增處理方法 */
private long tryIncReaderOverflow(long s) {
// assert (s & ABITS) >= RFULL;
if ((s & ABITS) == RFULL) {
if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) {
++readerOverflow;
state = s;
return s;
}
}
else if ((LockSupport.nextSecondarySeed() &
OVERFLOW_YIELD_RATE) == 0)
Thread.yield();
return 0L;
}
/** StampedLock鎖-讀鎖溢出遞減處理方法 */
private long tryDecReaderOverflow(long s) {
// assert (s & ABITS) >= RFULL;
if ((s & ABITS) == RFULL) {
if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) {
int r; long next;
if ((r = readerOverflow) > 0) {
readerOverflow = r - 1;
next = s;
}
else
next = s - RUNIT;
state = next;
return next;
}
}
else if ((LockSupport.nextSecondarySeed() &
OVERFLOW_YIELD_RATE) == 0)
Thread.yield();
return 0L;
}
}
- tryIncReaderOverflow()方法:主要是實現對於鎖獲取自旋時最大重試次數的遞增運算。其中:
- 對於滿足_stamp_ >= RFULL條件時,利用compareAndSwapLong()方法來實現CAS操作加持修改狀態值。對於readerOverflow作自增運算後返回一個_stamp,可能存在更新和釋放操作。_
- 否則,利用LockSupport.nextSecondarySeed() 判斷,對於線程做讓步處理,默認返回0
- tryDecReaderOverflow()方法:主要是實現對於鎖獲取自旋時最大重試次數的遞減運算。其中:
- 對於滿足_stamp_ == RFULL條件時,利用compareAndSwapLong()方法來實現CAS操作加持修改狀態值。對於readerOverflow>0做遞減運算後返回一個_stamp,可能存在更新和釋放操作。_
- 否則,利用LockSupport.nextSecondarySeed() 判斷,對於線程做讓步處理,默認返回0
2.4 基於Lock接口實現的ReadLockView
/** StampedLock鎖-最早在JDK1.8中實現的 */
public class StampedLock implements java.io.Serializable {
/** StampedLock鎖-ReadLockView */
final class ReadLockView implements Lock {
/** StampedLock鎖-獲取鎖 */
public void lock() { readLock(); }
/** StampedLock鎖-獲取可中斷鎖 */
public void lockInterruptibly() throws InterruptedException {
readLockInterruptibly();
}
/** StampedLock鎖-嘗試獲取鎖 */
public boolean tryLock() { return tryReadLock() != 0L; }
/** StampedLock鎖-嘗試獲取可超時鎖 */
public boolean tryLock(long time, TimeUnit unit)
throws InterruptedException {
return tryReadLock(time, unit) != 0L;
}
/** StampedLock鎖-釋放 */
public void unlock() { unstampedUnlockRead(); }
/** StampedLock鎖-不支持條件變量定義 */
public Condition newCondition() {
throw new UnsupportedOperationException();
}
}
/** StampedLock鎖-實例化ReadLock方法 */
public Lock asReadLock() {
ReadLockView v;
return ((v = readLockView) != null ? v :
(readLockView = new ReadLockView()));
}
/** StampedLock鎖-實例化ReadLock方法 */
public long readLock() {
long s = state, next; // bypass acquireRead on common uncontended case
return ((whead == wtail && (s & ABITS) < RFULL &&
U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) ?
next : acquireRead(false, 0L));
}
/** StampedLock鎖-實例化ReadLock方法 */
public long tryReadLock() {
for (;;) {
long s, m, next;
if ((m = (s = state) & ABITS) == WBIT)
return 0L;
else if (m < RFULL) {
if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
return next;
}
else if ((next = tryIncReaderOverflow(s)) != 0L)
return next;
}
}
/** StampedLock鎖-實例化ReadLock方法 */
public long tryReadLock(long time, TimeUnit unit)
throws InterruptedException {
long s, m, next, deadline;
long nanos = unit.toNanos(time);
if (!Thread.interrupted()) {
if ((m = (s = state) & ABITS) != WBIT) {
if (m < RFULL) {
if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
return next;
}
else if ((next = tryIncReaderOverflow(s)) != 0L)
return next;
}
if (nanos <= 0L)
return 0L;
if ((deadline = System.nanoTime() + nanos) == 0L)
deadline = 1L;
if ((next = acquireRead(true, deadline)) != INTERRUPTED)
return next;
}
throw new InterruptedException();
}
/** StampedLock鎖-釋放鎖方法 */
final void unstampedUnlockRead() {
// 自旋操作
for (;;) {
long s, m; WNode h;
if ((m = (s = state) & ABITS) == 0L || m >= WBIT)
throw new IllegalMonitorStateException();
else if (m < RFULL) {
if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
if (m == RUNIT && (h = whead) != null && h.status != 0)
release(h);
break;
}
}
else if (tryDecReaderOverflow(s) != 0L)
break;
}
}
}
對於ReadLock的實現,主要包含以下幾個方面的內容,其中:
- 基本實現方式:基於Lock接口實現,提供了對應的鎖獲取和釋放操作方法,其中:
- lock()方法:一般模式,主要通過StampedLock類中readLock()方法實現
- lockInterruptibly()方法:可中斷模式,主要通過StampedLock類中readLockInterruptibly()方法實現
- 無參數tryLock() 方法:嘗試獲取鎖,主要依據StampedLock類中tryReadLock() != 0L來實現
- 有參數tryLock() 方法:嘗試獲取鎖,主要依據StampedLock類中tryReadLock(long time, TimeUnit unit)!= 0L來實現
- unlock()方法:鎖的釋放,主要通過StampedLock類中unstampedUnlockRead()方法實現
- newCondition() 方法:不支持條件變量的定義,默認設置拋出UnsupportedOperationException
- 對應處理方法:主要是在StampedLock外層實現的操作方法,其中:
- readLock()方法:讀鎖的實現,主要核心邏輯在acquireRead()方法
- tryReadLock()方法:嘗試獲取讀鎖,核心處理邏輯是根據對應的條件返回對應的鎖的_stamp,否則拋出_InterruptedException。
- readLockInterruptibly()方法:讀鎖的可中斷機制實現,核心處理邏輯是判斷線程是否中斷以及利用acquireRead方法驗證,條件成立時,返回鎖的_stamp,否則拋出_InterruptedException。
- unstampedUnlockRead()方法:釋放鎖,核心處理邏輯自旋操作+compareAndSwapLong實現。
2.5 基於Lock接口實現的WriteLockView
/** StampedLock鎖-最早在JDK1.8中實現的 */
public class StampedLock implements java.io.Serializable {
final class WriteLockView implements Lock {
public void lock() { writeLock(); }
public void lockInterruptibly() throws InterruptedException {
writeLockInterruptibly();
}
public boolean tryLock() { return tryWriteLock() != 0L; }
public boolean tryLock(long time, TimeUnit unit)
throws InterruptedException {
return tryWriteLock(time, unit) != 0L;
}
public void unlock() { unstampedUnlockWrite(); }
public Condition newCondition() {
throw new UnsupportedOperationException();
}
}
/** StampedLock鎖-實例化WriteLock方法 */
public Lock asWriteLock() {
WriteLockView v;
return ((v = writeLockView) != null ? v :
(writeLockView = new WriteLockView()));
}
}
對於WriteLockView的實現,主要包含以下幾個方面的內容,其中:
- 基本實現方式:基於Lock接口實現,提供了對應的鎖獲取和釋放操作方法,其中:
- lock()方法:一般模式,主要通過StampedLock類中WriteLock()方法實現
- lockInterruptibly()方法:可中斷模式,主要通過StampedLock類中writeLockInterruptibly()方法實現
- 無參數tryLock() 方法:嘗試獲取鎖,主要依據StampedLock類中tryWriteLock() != 0L來實現
- 有參數tryLock() 方法:嘗試獲取鎖,主要依據StampedLock類中tryWriteLock(long time, TimeUnit unit) != 0L來實現
- unlock()方法:鎖的釋放,主要通過StampedLock類中unstampedUnlockWrite()方法實現
- newCondition() 方法:不支持條件變量的定義,默認設置拋出UnsupportedOperationException
- 核心處理方法:主要是在StampedLock外層實現的操作方法,其中:
- writeLock()方法:寫鎖的實現,主要核心邏輯在acquireWrite()方法
- tryWriteLock()方法:嘗試獲取寫鎖,核心處理邏輯是根據對應的條件返回對應的鎖的_stamp,否則拋出_InterruptedException。
- writeLockInterruptibly()方法:寫鎖的可中斷機制實現,核心處理邏輯是判斷線程是否中斷以及利用acquireWrite方法驗證,條件成立時,返回鎖的_stamp,否則拋出_InterruptedException。
- unstampedUnlockWrite()方法:釋放鎖,核心處理邏輯主要是通過調用release(WNode h) 方法實現。
2.6 基於ReadWriteLock接口實現ReadWriteLockView
/** StampedLock鎖-最早在JDK1.8中實現的 */
public class StampedLock implements java.io.Serializable {
/** StampedLock鎖-實例化ReadWriteLock方法 */
final class ReadWriteLockView implements ReadWriteLock {
/** StampedLock鎖-ReadLock方法 */
public Lock readLock() { return asReadLock(); }
/** StampedLock鎖-WriteLock方法 */
public Lock writeLock() { return asWriteLock(); }
}
/** StampedLock鎖-實例化ReadWriteLock方法 */
public ReadWriteLock asReadWriteLock() {
ReadWriteLockView v;
return ((v = readWriteLockView) != null ? v :
(readWriteLockView = new ReadWriteLockView()));
}
}
對於ReadWriteLockView的實現,主要包含兩個部分,其中:
- 基於ReadWriteLock接口實現,主要是實現readLock()和writeLock()方法
- 在asReadWriteLock()方法中,實例化ReadWriteLockView對象
3. 具體實現
對於StampedLock的具體實現,我們可以從如下幾個方面拆解開來分析:
- 共享鎖ReadLock鎖獲取操作實現: 需要區分悲觀讀鎖和樂觀讀鎖的獲取個有不同,一般有默認獲取方式和嘗試獲取兩種方式。
- 獨佔鎖WriteLock寫鎖獲取操作實現: 寫鎖與悲觀讀鎖互斥,一般有默認獲取方式和嘗試獲取兩種方式
- 共享鎖ReadLock鎖釋放操作實現: 一般分爲全釋放和半釋放ReadLock鎖操作兩種方式
- 獨佔鎖WriteLock鎖釋放操作實現:一般分爲全釋放和半釋放WriteLock鎖操作兩種方式
接下來,我們便從具體的代碼中來分析以上內容的基本實現,以方便我們正確認識和了解StampedLock鎖。
3.1 共享鎖ReadLock讀鎖獲取操作實現
/** StampedLock鎖-最早在JDK1.8中實現的 */
public class StampedLock implements java.io.Serializable {
/** StampedLock鎖-悲觀讀鎖-嘗試獲取鎖(默認模式,不支持超時機制) */
public long tryReadLock() {
// 鎖自旋轉+compareAndSwapLong來CAS操作加持
for (;;) {
long s, m, next;
// [1].直接返回0
if ((m = (s = state) & ABITS) == WBIT)
return 0L;
// [2].compareAndSwapLong來CAS操作加持
else if (m < RFULL) {
if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
return next;
}
// [3].嘗試獲取讀鎖溢出處理
else if ((next = tryIncReaderOverflow(s)) != 0L)
return next;
}
}
/** StampedLock鎖-悲觀讀鎖-嘗試獲取鎖(指定模式,支持超時機制) */
public long tryReadLock(long time, TimeUnit unit)
throws InterruptedException {
long s, m, next, deadline;
long nanos = unit.toNanos(time);
if (!Thread.interrupted()) {
if ((m = (s = state) & ABITS) != WBIT) {
if (m < RFULL) {
if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
return next;
}
else if ((next = tryIncReaderOverflow(s)) != 0L)
return next;
}
if (nanos <= 0L)
return 0L;
if ((deadline = System.nanoTime() + nanos) == 0L)
deadline = 1L;
if ((next = acquireRead(true, deadline)) != INTERRUPTED)
return next;
}
throw new InterruptedException();
}
/** StampedLock鎖-樂觀讀鎖-嘗試獲取鎖 */
public long tryOptimisticRead() {
long s;
return (((s = state) & WBIT) == 0L) ? (s & SBITS) : 0L;
}
}
對於讀鎖的獲取來說,都屬於是共享鎖,主要提供了以下幾種方式:
- 無參數tryReadLock()方法:悲觀讀鎖的獲取方式,默認模式,不支持超時機制
- 有參數tryReadLock()方法:悲觀讀鎖的獲取方式,指定參數模式,支持超時機制
- 無參數tryOptimisticRead()方法:樂觀讀鎖的獲取方式,沒有加鎖操作
3.2 獨佔鎖WriteLock寫鎖獲取操作實現
/** StampedLock鎖-最早在JDK1.8中實現的 */
public class StampedLock implements java.io.Serializable {
/** StampedLock鎖-獲取寫鎖操作(不支持超時機制) */
public long tryWriteLock() {
long s, next;
return ((((s = state) & ABITS) == 0L &&
U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
next : 0L);
}
/** StampedLock鎖-獲取寫鎖操作(支持超時機制) */
public long tryWriteLock(long time, TimeUnit unit)
throws InterruptedException {
long nanos = unit.toNanos(time);
if (!Thread.interrupted()) {
long next, deadline;
if ((next = tryWriteLock()) != 0L)
return next;
if (nanos <= 0L)
return 0L;
if ((deadline = System.nanoTime() + nanos) == 0L)
deadline = 1L;
if ((next = acquireWrite(true, deadline)) != INTERRUPTED)
return next;
}
throw new InterruptedException();
}
}
對於寫鎖的獲取來說,都屬於是獨佔鎖,主要提供了以下幾種方式:
- 無參數tryWriteLock()方法:默認模式,不支持超時機制
- 有參數tryWriteLock()方法:指定模式,依據參數來實現,支持超時機制
3.3 共享鎖ReadLock釋放操作實現
/** StampedLock鎖-最早在JDK1.8中實現的 */
public class StampedLock implements java.io.Serializable {
/** StampedLock鎖-釋放鎖操作 */
public void unlock(long stamp) {
long a = stamp & ABITS, m, s; WNode h;
while (((s = state) & SBITS) == (stamp & SBITS)) {
if ((m = s & ABITS) == 0L)
break;
else if (m == WBIT) {
if (a != m)
break;
state = (s += WBIT) == 0L ? ORIGIN : s;
if ((h = whead) != null && h.status != 0)
release(h);
return;
}
else if (a == 0L || a >= WBIT)
break;
else if (m < RFULL) {
if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
if (m == RUNIT && (h = whead) != null && h.status != 0)
release(h);
return;
}
}
else if (tryDecReaderOverflow(s) != 0L)
return;
}
throw new IllegalMonitorStateException();
}
/** StampedLock鎖-釋放讀鎖 */
public void unlockRead(long stamp) {
long s, m; WNode h;
for (;;) {
if (((s = state) & SBITS) != (stamp & SBITS) ||
(stamp & ABITS) == 0L || (m = s & ABITS) == 0L || m == WBIT)
throw new IllegalMonitorStateException();
if (m < RFULL) {
if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
if (m == RUNIT && (h = whead) != null && h.status != 0)
release(h);
break;
}
}
else if (tryDecReaderOverflow(s) != 0L)
break;
}
}
/** StampedLock鎖-悲觀讀鎖-轉換升級並釋放處理 */
public long tryConvertToReadLock(long stamp) {
long a = stamp & ABITS, m, s, next; WNode h;
while (((s = state) & SBITS) == (stamp & SBITS)) {
if ((m = s & ABITS) == 0L) {
if (a != 0L)
break;
else if (m < RFULL) {
if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
return next;
}
else if ((next = tryIncReaderOverflow(s)) != 0L)
return next;
}
else if (m == WBIT) {
if (a != m)
break;
state = next = s + (WBIT + RUNIT);
if ((h = whead) != null && h.status != 0)
release(h);
return next;
}
else if (a != 0L && a < WBIT)
return stamp;
else
break;
}
return 0L;
}
/** StampedLock鎖-樂觀讀鎖-轉換升級並釋放處理 */
public long tryConvertToOptimisticRead(long stamp) {
long a = stamp & ABITS, m, s, next; WNode h;
U.loadFence();
for (;;) {
if (((s = state) & SBITS) != (stamp & SBITS))
break;
if ((m = s & ABITS) == 0L) {
if (a != 0L)
break;
return s;
}
else if (m == WBIT) {
if (a != m)
break;
state = next = (s += WBIT) == 0L ? ORIGIN : s;
if ((h = whead) != null && h.status != 0)
release(h);
return next;
}
else if (a == 0L || a >= WBIT)
break;
else if (m < RFULL) {
if (U.compareAndSwapLong(this, STATE, s, next = s - RUNIT)) {
if (m == RUNIT && (h = whead) != null && h.status != 0)
release(h);
return next & SBITS;
}
}
else if ((next = tryDecReaderOverflow(s)) != 0L)
return next & SBITS;
}
return 0L;
}
public boolean tryUnlockRead() {
long s, m; WNode h;
while ((m = (s = state) & ABITS) != 0L && m < WBIT) {
if (m < RFULL) {
if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
if (m == RUNIT && (h = whead) != null && h.status != 0)
release(h);
return true;
}
}
else if (tryDecReaderOverflow(s) != 0L)
return true;
}
return false;
}
/** StampedLock鎖-嘗試釋放讀鎖 */
public boolean tryUnlockRead() {
long s, m; WNode h;
while ((m = (s = state) & ABITS) != 0L && m < WBIT) {
if (m < RFULL) {
if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
if (m == RUNIT && (h = whead) != null && h.status != 0)
release(h);
return true;
}
}
else if (tryDecReaderOverflow(s) != 0L)
return true;
}
return false;
}
}
對於讀鎖的釋放來說,主要提供了以下幾種方式:
- unlock() 方法:依據鎖的狀態status來匹配對應鎖的stamp,然後釋放鎖操作
- unlockRead()方法: 依據鎖的狀態status來匹配對應讀鎖的stamp,然後釋放鎖操作
- tryUnlockRead()方法:釋放當前持有的讀鎖,會設置一個stamp然後返回true,否則,返回false
- tryConvertToReadLock()方法:依據鎖的狀態status來匹配對應讀鎖的stamp,然後根據對應情況處理。其中:
- 單寫鎖模式:一般返回一個對應讀鎖的stamp
- 悲觀讀模式:直接返回對應讀鎖的stamp
- 樂觀讀模式:需要獲取一個讀鎖,然後是立即返回對應讀鎖的stamp
- tryConvertToOptimisticRead(): 依據鎖的狀態status來匹配對應讀鎖的stamp,然後轉換升級處理釋放。其中:
- 悲觀讀模式:屬於一般讀鎖模式,返回的是檢測到對應讀鎖的stamp
- 樂觀讀模式:需要返回通過驗證的對應讀鎖的stamp
3.4 獨佔鎖WriteLock寫鎖釋放操作實現
/** StampedLock鎖-最早在JDK1.8中實現的 */
public class StampedLock implements java.io.Serializable {
/** StampedLock鎖-鎖釋放方法(一般方法) */
public void unlock(long stamp) {
long a = stamp & ABITS, m, s; WNode h;
while (((s = state) & SBITS) == (stamp & SBITS)) {
if ((m = s & ABITS) == 0L)
break;
else if (m == WBIT) {
if (a != m)
break;
state = (s += WBIT) == 0L ? ORIGIN : s;
if ((h = whead) != null && h.status != 0)
release(h);
return;
}
else if (a == 0L || a >= WBIT)
break;
else if (m < RFULL) {
if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
if (m == RUNIT && (h = whead) != null && h.status != 0)
release(h);
return;
}
}
else if (tryDecReaderOverflow(s) != 0L)
return;
}
throw new IllegalMonitorStateException();
}
/** StampedLock鎖-寫鎖釋放方法 */
public void unlockWrite(long stamp) {
WNode h;
if (state != stamp || (stamp & WBIT) == 0L)
throw new IllegalMonitorStateException();
state = (stamp += WBIT) == 0L ? ORIGIN : stamp;
if ((h = whead) != null && h.status != 0)
release(h);
}
/** StampedLock鎖-寫鎖轉換升級處理並釋放鎖 */
public long tryConvertToWriteLock(long stamp) {
long a = stamp & ABITS, m, s, next;
while (((s = state) & SBITS) == (stamp & SBITS)) {
if ((m = s & ABITS) == 0L) {
if (a != 0L)
break;
if (U.compareAndSwapLong(this, STATE, s, next = s + WBIT))
return next;
}
else if (m == WBIT) {
if (a != m)
break;
return stamp;
}
else if (m == RUNIT && a != 0L) {
if (U.compareAndSwapLong(this, STATE, s,
next = s - RUNIT + WBIT))
return next;
}
else
break;
}
return 0L;
}
/** StampedLock鎖-unlockWrite的核心實現 */
private void release(WNode h) {
if (h != null) {
WNode q; Thread w;
U.compareAndSwapInt(h, WSTATUS, WAITING, 0);
if ((q = h.next) == null || q.status == CANCELLED) {
for (WNode t = wtail; t != null && t != h; t = t.prev)
if (t.status <= 0)
q = t;
}
if (q != null && (w = q.thread) != null)
U.unpark(w);
}
}
}
對於寫鎖的釋放來說,主要提供了以下種方式:
- unlock() 方法:依據鎖的狀態status來匹配對應鎖的stamp,然後釋放鎖操作
- unlockWrite()方法:依據鎖的狀態status來匹配對應寫鎖的stamp,然後釋放鎖操作
- tryUnlockWrite()方法:釋放當前持有的寫鎖,會設置一個stamp然後返回true,否則,返回false
- tryConvertToWriteLock()方法:依據鎖的狀態status來匹配stamp,根據對應鎖的做升級處理。其中:
- 單寫鎖模式:直接返回對應的寫鎖標記stamp
- 讀寫鎖模式:需要釋放讀鎖鎖,並返回對應的寫鎖標記stamp
- 樂觀讀模式:直接返回對應的寫鎖標記stamp
綜上所述,StampedLock鎖本質上依然是一種讀寫鎖,只是沒有基於AQS基礎同步器來實現,是自定義封裝API操作實現的。
寫在最後
通過對Java領域中,JDK內部提供的各種鎖的實現來看,一直圍繞的核心主要還是基於AQS基礎同步器來實現的,但是AQS基礎同步器不是一種非它不可的技術標準規範,更多的只是一套技術參考指南。
但是,實際上,Java對於鎖的實現與運用遠遠不止這些,還有相位器(Phaser)和交換器(Exchanger),以及在Java JDK1.8版本之前併發容器ConcurrentHashMap中使用的分段鎖(Segment)。
不論是何種實現和應用,在Java併發編程領域來講,都是圍繞線程安全問題的角度去考慮的,只是針對於各種各樣的業務場景做的具體的實現。
一定意義上來講,對線程加鎖只是併發編程的實現方式之一,相對於實際應用來說,Java領域中的鎖都只是一種單一應用的鎖,只是給我們掌握Java併發編程提供一種思想沒,三言兩語也不可能詳盡。
到此爲止,這算是對於Java領域中併發鎖的最終章,文中表述均爲個人看法和個人理解,如有不到之處,忘請諒解也請給予批評指正。
最後,技術研究之路任重而道遠,願我們熬的每一個通宵,都撐得起我們想在這條路上走下去的勇氣,未來仍然可期,與各位程序編程君共勉!