介紹
StampedLock 是一種讀寫鎖,但是是 JUC 鎖中比較特殊的一個。因爲它的實現邏輯不依賴 AbstractQueuedSynchronizer,線程等待和喚醒也不依賴 LockSupport(而是直接使用 Unsafe.park 和 unpark 方法。當然 LockSupport 本身也是使用 Unsafe.park 和 unpark 方法實現的的線程阻塞和喚醒)。StampedLock 不依賴 JUC 包中的其他類,獨自實現了讀寫鎖的功能。鑑於此沒有將 StampedLock 放在鎖筆記中一起分析它的源碼。
StampedLock 雖然是讀寫鎖,但是比起 JUC 下的讀寫鎖,StampedLock 提供了一種樂觀讀的方法。並且獲取鎖的過程中,大量使用自旋和 CAS 修改的方式獲取鎖,理論上比 ReentrantReadWriteLock 擁有更高的併發性能。
但是也是由於 StampedLock 大量使用自旋的原因(ReentrantReadWriteLock 也使用了自旋,但是沒有 StampedLock 頻繁),CPU 的消耗理論上也比 ReentrantReadWriteLock 高。
StampedLock 非常適合寫鎖中的操作非常快的業務場景。因爲讀鎖如果因爲寫鎖而獲取鎖失敗,讀鎖會做重試獲取和有限次的自旋的方式,比較晚進入到等待隊列中。如果在自旋過程中,寫鎖能釋放,那麼獲取讀鎖的線程就能避免被操作系統阻塞和喚醒等耗資源操作,增加讀鎖的響應效率。
StampedLock 和 ReentrantReadWriteLock 相比使用上需要格外注意。StampedLock 嚴重依賴版本號,需要嚴格按照要求使用這個版本號,並且 StampedLock 不支持重入。
StampedLock 提供三種方式使用讀寫鎖,分別是寫、讀和樂觀度。StampedLock 也同步器一樣也有一個 state 保存鎖的信息。同步器中主要保存重入次數,而 StampedLock 保存版本信息和正在被使用的上訴三種方式的信息,即線程使用了三種方式中的一種後,信息會被記錄到 state 中。StampedLock 提供的 try*方法如果成功獲取鎖則返回版本號,如果獲取失敗則返回 0。
- 寫:writeLock 方法如果當下獲取鎖失敗,線程被被阻塞(這裏的阻塞包括了線程自旋和操作系統級別的線程阻塞)直到獲取鎖成功。writeLock 方法會返回一個版本號,用於釋放鎖 unlockWrite(long stamp)用。如果被上了寫鎖,其他線程在獲取樂觀讀鎖時都會失敗(即返回的版本號爲 0)。
- 讀:readLock 方法如果當前獲取鎖失敗,線程被阻塞(同樣也包括自旋和操作系統級別的線程阻塞)知道獲取鎖成功。
- 樂觀讀(Optimistic Reading):tryOptimisticRead 如果有其他線程獲取了寫鎖,那麼返回 0,否則返回版本號。這個版本號可能會在釋放樂觀讀之前發生改變。如果獲取了樂觀讀鎖,此時還未釋放,但是其他線程獲取了寫鎖,那麼之前獲取到的樂觀讀的版本號不是最新的版本號了。但是樂觀讀的性能非常好,非常適合耗時非常少代碼塊。一般通過樂觀讀上鎖後讀取數據保存在本地變量中,然後通過校驗版本號來檢查這部分的數據是否還有效(如果版本號一致,說明數據未被修改)。
如果當前已經上了寫鎖,或者上了讀鎖並且沒有其他線程獲取過讀鎖,或者樂觀讀並且版本號是有效的,那麼通過 tryConvertToWriteLock 做鎖升級都是允許的。
StampedLock 沒有公平策略,所有的獲取都是盡最大的努力獲取到鎖。
官方的使用例子如下:
class Point {
private double x, y;
private final StampedLock sl = new StampedLock();
/**
* 寫鎖的例子
*/
void move(double deltaX, double deltaY) {
//獲取寫鎖。如果失敗線程阻塞知道獲取鎖。
//stamp 是獲取到鎖後的版本號,釋放鎖時需要使用到這個值。
//不要改動這個值內容
long stamp = sl.writeLock();
try {
x += deltaX;
y += deltaY;
} finally {
//釋放寫鎖
sl.unlockWrite(stamp);
}
}
/**
* 樂觀讀例子
*/
double distanceFromOrigin() {
//使用樂觀讀並獲取版本號
//樂觀讀不需要釋放鎖的操作
long stamp = sl.tryOptimisticRead();
double currentX = x, currentY = y;
//如果版本號不可用,說明數據已經被修改了。一般建議是使用上讀鎖再次獲取數據。
if (!sl.validate(stamp)) {
//上讀鎖並返回最新版本號信息
stamp = sl.readLock();
try {
currentX = x;
currentY = y;
} finally {
//釋放讀鎖
sl.unlockRead(stamp);
}
}
return Math.sqrt(currentX * currentX + currentY * currentY);
}
//鎖升級例子
void moveIfAtOrigin(double newX, double newY) {
//上讀鎖並返回版本號
long stamp = sl.readLock();
try {
while (x == 0.0 && y == 0.0) {
//升級鎖成寫鎖。如果有其他線程正在使用讀鎖,那麼當前線程阻塞直到其他線程釋放完讀鎖。
long ws = sl.tryConvertToWriteLock(stamp);
if (ws != 0L) {
stamp = ws;
x = newX;
y = newY;
break;
}
else {
//釋放讀鎖
sl.unlockRead(stamp);
//獲取寫鎖
stamp = sl.writeLock();
}
}
} finally {
//釋放鎖
sl.unlock(stamp);
}
}
}
源碼分析
StampedLock 和 AbstractQueuedSynchronizer 類似,利用內置的 WNode 構建一個雙向鏈表用於存儲等待線程。與 AbstractQueuedSynchronizer 不同的是 WNode 還有一個縱向存儲同時獲取讀鎖的線程,方便前面寫鎖釋放後同時喚醒排在後面的獲取讀鎖阻塞的線程。WNode構成的隊列如下所示:
源碼與說明如下如下:
public class StampedLock implements java.io.Serializable {
/**
* Java 虛擬機的可用的處理器數量,用於計算自旋次數
* 這個值服務器的 CPU 核數量並不完全一致,它 和 CPU 核數,超線程數量相關,但是又不是簡單的乘積。
*/
private static final int NCPU = Runtime.getRuntime().availableProcessors();
/**
* 獲取鎖失敗後準備進入等待隊列前的自旋次數
*/
private static final int SPINS = (NCPU > 1) ? 1 << 6 : 0;
/**
* 如果當前等待隊列的第二個節點(快到當前獲取到鎖了),那麼自旋等待。這個參數是這個場景下的自旋次數
*/
private static final int HEAD_SPINS = (NCPU > 1) ? 1 << 10 : 0;
/**
* 第二次阻塞最自旋次數
*/
private static final int MAX_HEAD_SPINS = (NCPU > 1) ? 1 << 16 : 0;
/**
* 線程是否讓出 CPU 的比較參數
*/
private static final int OVERFLOW_YIELD_RATE = 7;
/**
* state 用於存儲讀寫鎖標記的分割位 1 << 7 存儲寫鎖標記,低 7 位存儲讀鎖次數
*/
private static final int LG_READERS = 7;
/**
* 0000 0001,讀鎖變更單位,即獲取讀鎖時,低 6 位加 1。
*/
private static final long RUNIT = 1L;
/**
* 1000 0000,寫鎖標記。當 state & WBIT = WBIT 時,有線程持有寫鎖。1000 0000。
*/
private static final long WBIT = 1L << LG_READERS;
/**
* 0111 1111,讀狀態標識全爲 1 時的值,方便做位運算,判斷是否讀鎖溢出
*/
private static final long RBITS = WBIT - 1L;
/**
* 0111 1110 讀鎖上限
*/
private static final long RFULL = RBITS - 1L;
/**
* 1111 1111 讀寫鎖全部爲 1,方便做位運算,判斷未上鎖(s & ABITS == 0),讀鎖溢出等((s & ABITS) >= RFULL)
*/
private static final long ABITS = RBITS | WBIT;
/**
* 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1000 0000 RBITS 取反的結果,方便獲取寫鎖以及更高位數的值
*/
private static final long SBITS = ~RBITS;
// 1 0000 0000 state 初始化的值,和獲取鎖失敗返回的 0 值做區分。
private static final long ORIGIN = WBIT << 1;
// 線程中斷的標識。
private static final long INTERRUPTED = 1L;
// 節點的狀態
private static final int WAITING = -1;
private static final int CANCELLED = 1;
// 節點的類型
private static final int RMODE = 0;
private static final int WMODE = 1;
/** 節點的類結構 */
static final class WNode {
volatile WNode prev;
volatile WNode next;
volatile WNode cowait; // 讀節點的隊列
volatile Thread thread; // 線程
volatile int status; // 節點狀態。0 表示是初始狀態,-1 是等待狀態,1 是取消狀態
final int mode; // RMODE or WMODE
WNode(int m, WNode p) { mode = m; prev = p; }
}
/**
* 指向隊列的第一個節點
*/
private transient volatile WNode whead;
//等待隊列的最後一個節點
private transient volatile WNode wtail;
// 讀寫鎖視圖。
transient ReadLockView readLockView;
transient WriteLockView writeLockView;
transient ReadWriteLockView readWriteLockView;
/**
* 狀態。保存寫鎖和讀鎖信息
*/
private transient volatile long state;
// 如果讀鎖達到 state 中能夠存儲的上限後,溢出的讀鎖數量保存在 readerOverflow 中
private transient int readerOverflow;
/**
* 構造函數。將 state 值初始化成 ORIGIN 1 0000 0000。區分獲取鎖失敗後返回的 0 值。
*/
public StampedLock() {
state = ORIGIN;
}
/**
* 上寫鎖。如果上鎖失敗會阻塞
*
* @return 版本號。釋放鎖和改變模式(寫鎖變讀鎖)時用到
*/
public long writeLock() {
long s, next;
/**
* ((s = state) & ABITS) == 0 未上鎖的狀態條件才存在
* 如果未上過鎖,那麼通過 CAS 更新寫鎖標誌位。如果成功返回新的版本號。初始情況下爲 1 1000 0000
* CAS 更新失敗(多個線程通過獲取鎖)則調用 acquireWrite 方法獲取鎖並返回版本號。
*/
return ((((s = state) & ABITS) == 0L &&
U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
next : acquireWrite(false, 0L));
}
/**
* 嘗試獲取一個寫鎖。如果獲取成功則返回一個版本號(大於 0 的長整形數值),失敗則返回 0
* 獲取沒有成功獲取鎖不會阻塞線程
* @return 0 或者一個版本號
*/
public long tryWriteLock() {
long s, next;
return ((((s = state) & ABITS) == 0L &&
U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
next : 0L);
}
/**
* 嘗試獲取鎖。如果在指定的時間內並且線程未被中斷獲取鎖失敗,線程會被阻塞(包括自旋)。
*
* @param 如果獲取鎖失敗則阻塞,阻塞的最長時間。超過這個時間線程被喚醒並且獲取鎖失敗(返回 0)
* @param 等待時間的單位。
* @return 0 或者版本號
* @throws 如果線程阻塞拋出 InterruptedException
*/
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();
}
/**
* 嘗試獲取鎖。如果獲取失敗線程阻塞(包括自旋)。如果線程中斷則拋出 InterruptedException
*
* @return a stamp that can be used to unlock or convert mode
* @throws InterruptedException if the current thread is interrupted
* before acquiring the lock
*/
public long writeLockInterruptibly() throws InterruptedException {
long next;
if (!Thread.interrupted() &&
(next = acquireWrite(true, 0L)) != INTERRUPTED)
return next;
throw new InterruptedException();
}
/**
* 獲取讀鎖。如果獲取成功則返回版本號,如果獲取失敗則線程阻塞。
*
* @return 版本號
*/
public long readLock() {
long s = state, next;
/**
* whead == wtatil 表示隊列中沒有等待線程
* (s & ABITS) < RFULL 沒有上寫鎖並且鎖未溢出
* CAS 修改成功
* 如果以上條件都成立,返回版本號 next = s + RUNIT,否則通過調用 acquireRead 獲取鎖
*/
return ((whead == wtail && (s & ABITS) < RFULL &&
U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) ?
next : acquireRead(false, 0L));
}
/**
* 獲取讀鎖。如果當前獲取到鎖,則立即返回版本號,如果無法獲取到鎖則自旋或者線程阻塞。
*
* @return a stamp that can be used to unlock or convert mode,
* or zero if the lock is not available
*/
public long tryReadLock() {
//自旋獲取鎖
for (;;) {
long s, m, next;
// 如果已經上寫鎖,那麼獲取失敗,返回 0
if ((m = (s = state) & ABITS) == WBIT)
return 0L;
//如果未超過讀鎖上限,那麼 CAS 更新上讀鎖
else if (m < RFULL) {
if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
return next;
}
// 超過讀鎖上限,調用 tryIncReaderOverflow 方法處理上限部分的讀鎖次數存儲
else if ((next = tryIncReaderOverflow(s)) != 0L)
return next;
}
}
/**
* 獲取讀鎖。支持超時時間和響應線程中斷。
*
* @param time the maximum time to wait for the lock
* @param unit the time unit of the {@code time} argument
* @return a stamp that can be used to unlock or convert mode,
* or zero if the lock is not available
* @throws InterruptedException if the current thread is interrupted
* before acquiring the lock
*/
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();
}
/**
* 獲取讀鎖。該方法響應線程中斷。
*
* @return a stamp that can be used to unlock or convert mode
* @throws InterruptedException if the current thread is interrupted
* before acquiring the lock
*/
public long readLockInterruptibly() throws InterruptedException {
long next;
if (!Thread.interrupted() &&
(next = acquireRead(true, 0L)) != INTERRUPTED)
return next;
throw new InterruptedException();
}
/**
* 樂觀讀。
* 通過判斷是否上寫鎖快速返回一個版本號或者 0。如果上寫鎖了返回 0,否則返回一個版本號(讀鎖位數的取反值)。
* 由於樂觀讀返回的版本號是固定的值,所以任何樂觀讀返回的版本號都是一樣的
*
* @return a stamp, or zero if exclusively locked
*/
public long tryOptimisticRead() {
long s;
return (((s = state) & WBIT) == 0L) ? (s & SBITS) : 0L;
}
/**
* 校驗版本號
*
* @param stamp a stamp
* @return {@code true} if the lock has not been exclusively acquired
* since issuance of the given stamp; else false
*/
public boolean validate(long stamp) {
// 加入內存屏障,確保 state 是最新的值
U.loadFence();
// SBITS 讀位數取反,(stamp & SBITS) == (state & SBITS) 表示忽略讀鎖看其他位數是否發生變化,如果發生變化則返回 false,未發生變化則返回 true
return (stamp & SBITS) == (state & SBITS);
}
/**
* 如果版本號不正確則拋出 IllegalMonitorStateException 異常,正確則釋放寫鎖
*
*
* @param stamp a stamp returned by a write-lock operation
* @throws IllegalMonitorStateException if the stamp does
* not match the current state of this lock
*/
public void unlockWrite(long stamp) {
WNode h;
/**
* state != stamp 版本號發生變化
* (stamp & WBIT) == 0L 沒有上寫鎖
* 由於是釋放寫鎖,在釋放前版本號不應該發生變化且應該是上過寫鎖的。不滿足以上要求則拋出異常
*/
if (state != stamp || (stamp & WBIT) == 0L)
throw new IllegalMonitorStateException();
// 寫位數+1 會向上進 1,寫位數變成 0,釋放鎖。如果向上進 1 後 state == 0 則表示溢出,那麼 state 設爲初始默認值 ORIGIN。否則爲寫位數加一後的結果
state = (stamp += WBIT) == 0L ? ORIGIN : stamp;
if ((h = whead) != null && h.status != 0)
release(h);
}
/**
* 如果版本號正確,則釋放讀鎖並喚醒後續節點。
*
* @param stamp a stamp returned by a read-lock operation
* @throws IllegalMonitorStateException if the stamp does
* not match the current state of this lock
*/
public void unlockRead(long stamp) {
long s, m; WNode h;
for (;;) {
/**
* (s = state) & SBITS) != (stamp & SBITS) 除讀鎖位數外,其他位數是否發生變化
* (stamp & ABITS) == 0L 之前獲取的版本號沒有上過讀鎖也沒有上過寫鎖
* (m = s & ABITS) == 0L || m == WBIT 當前版本號沒有讀鎖或者上了寫鎖
* 以上情況又發生則拋出 IllegalMonitorStateException
*/
if (((s = state) & SBITS) != (stamp & SBITS) ||
(stamp & ABITS) == 0L || (m = s & ABITS) == 0L || m == WBIT)
throw new IllegalMonitorStateException();
// 讀鎖未溢出
if (m < RFULL) {
// CAS 修改版本號
if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
/**
* m == RUNIT 表示當前只有個讀鎖,並且需要將這個唯一的讀鎖給釋放掉
* (h = whead) != nul 表示等待隊列中節點(鑑於當前運行是讀鎖的線程,所以等待隊列中 head 應該是寫鎖)
* h.status != 0 說明線程被阻塞而不是在自旋
* 以上情況都滿足,需要協助喚醒隊列中的節點
*/
if (m == RUNIT && (h = whead) != null && h.status != 0)
release(h);
break;
}
}
// 讀鎖未溢出,通過 tryDecReaderOverflow 維護版本號。如果 tryDecReaderOverflow 返回不爲 0 說明釋放鎖成功了,跳出自旋
else if (tryDecReaderOverflow(s) != 0L)
break;
}
}
/**
* 如果版本號正確,那麼釋放對應的鎖。
*
* @param stamp a stamp returned by a lock operation
* @throws IllegalMonitorStateException if the stamp does
* not match the current state of this lock
*/
public void unlock(long stamp) {
long a = stamp & ABITS, m, s; WNode h;
// ((s = state) & SBITS) == (stamp & SBITS) 表示寫標誌未發生變化
while (((s = state) & SBITS) == (stamp & SBITS)) {
// ((m = s & ABITS) == 0L) 表示未上任何鎖。跳出循環拋出異常
if ((m = s & ABITS) == 0L)
break;
// (m == WBIT 表示只有寫鎖。如果只有寫鎖,但是版本信息和只有寫鎖不相符,跳出循環拋出異常
else if (m == WBIT) {
if (a != m)
break
// 版本信息和只有寫鎖相符合,版本號加上一個寫鎖位數。如果 state 溢出(state == 0L),那麼等於初始化值,未溢出則爲加後 的結果
state = (s += WBIT) == 0L ? ORIGIN : s;
// 如果等待隊列不爲空並且頭結點的狀態不是初始化的狀態,那麼喚醒等待隊列的後續節點,之後釋放鎖操作結束。
if ((h = whead) != null && h.status != 0)
release(h);
return;
}
/**
* a == 0L 表示未上讀寫鎖
* a >= WBIT 即上了寫鎖又上了讀鎖
* 滿足以上任一條件則跳出循環拋出異常
*/
else if (a == 0L || a >= WBIT)
break;
// (m < RFULL 表示只上了讀鎖並且讀鎖數量未溢出
else if (m < RFULL) {
// 利用 CAS 方式讀鎖減一
if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
// 如果讀鎖只有一個並且等待隊列不爲空並且頭結點的狀態不是初始化的狀態,那麼喚醒等待隊列後續節點。
if (m == RUNIT && (h = whead) != null && h.status != 0)
release(h);
return;
}
}
// 溢出部分使用 tryDecReaderOverflow 方法處理釋放讀鎖。如果返回值不爲 0,則釋放成功完成釋放鎖操作;否則自旋
else if (tryDecReaderOverflow(s) != 0L)
return;
}
throw new IllegalMonitorStateException();
}
/**
* 升級爲寫鎖
* 首先驗證版本號是否有效,無效則返回 0。
* 如果只有一個寫鎖(當前線程已經獲取了寫鎖),立即返回當前版本號(版本號未發生變化)
* 如果只有一個讀鎖,去掉讀鎖換上寫鎖(state -1 + WBIT),返回新版本號
* 如果是樂觀讀,加上讀鎖(state + WBIT),返回新版本號
* 其他情況返回 0,升級失敗
*
* @param stamp a stamp
* @return a valid write stamp, or zero on failure
*/
public long tryConvertToWriteLock(long stamp) {
long a = stamp & ABITS, m, s, next;
/**
* ((s = state) & SBITS) == (stamp & SBITS) 表示獲取版本號到調用 tryConvertToWriteLock 期間讀鎖未發生變化
*/
while (((s = state) & SBITS) == (stamp & SBITS)) {
// (m = s & ABITS) == 0L 表示沒有任何讀寫鎖
if ((m = s & ABITS) == 0L) {
// a != 0L 表示之前獲取的版本號表明有讀寫鎖,當時 m==0L 說明,說明之前獲取的版本號已經過期,升級鎖失敗
if (a != 0L)
break;
// CAS 操作加寫鎖,並返回新的版本號。能做此操作說明沒有讀寫鎖,不需要考慮溢出的問題
if (U.compareAndSwapLong(this, STATE, s, next = s + WBIT))
return next;
}
// 說明有寫鎖
else if (m == WBIT) {
// a != m 說明在操作期間,版本已經發生變化,stamp 不是最新的版本號,跳出循環升級失敗
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;
}
/**
* 轉成讀鎖
* 版本號正確並且沒有任何鎖,那麼加一個讀鎖。
* 版本號正確並且讀鎖溢出,那麼使用 tryIncReaderOverflow 處理溢出部分
* 版本號正確並且只有一個寫鎖,那麼釋放寫鎖並且讀鎖加 1 ,並且喚醒後續線程。
* 版本號正確並且是樂觀讀(沒有任何鎖),那麼加上一個讀鎖
* 其他情況轉換讀鎖失敗
*
* @param stamp a stamp
* @return a valid read stamp, or zero on failure
*/
public long tryConvertToReadLock(long stamp) {
// a = stamp & ABITS a b 表示版本號中讀寫鎖的情況
long a = stamp & ABITS, m, s, next; WNode h;
// ((s = state) & SBITS) == (stamp & SBITS) 說明在做換讀鎖操作時,讀寫鎖位數未發生變化,版本號有效
while (((s = state) & SBITS) == (stamp & SBITS)) {
// m = s & ABITS) == 0L m 表示是當前讀寫鎖的情況,m == 0L 表示當前沒有任何鎖。
if ((m = s & ABITS) == 0L) {
// 鎖已經發生變化,鎖升級失敗
if (a != 0L)
break;
// 當前讀鎖未溢出
else if (m < RFULL) {
// CAS 修改鎖,成功則返回最新的版本號,否則自旋
if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
return next;
}
// 溢出則使用 tryIncReaderOverflow 處理溢出部分的讀鎖。成功則返回最新的版本號
else if ((next = tryIncReaderOverflow(s)) != 0L)
return next;
}
// m == WBIT 表示當下的版本號只有寫鎖
else if (m == WBIT) {
// a != m 表示狀態在這期間發生變化,跳出循環,轉變失敗。上了寫鎖後,釋放前不允許上讀鎖。a !=m 表明版本已經過期
if (a != m)
break;
// 更新版本號。s + (WBIT + RUNIT) 寫鎖位數加一後向上進一,寫鎖標誌消失,並且讀鎖加一
state = next = s + (WBIT + RUNIT);
// 如果等待隊列頭節點不爲空並且狀態不是初始化的狀態,說明等待隊列有後續的阻塞節點。由於是寫鎖
if ((h = whead) != null && h.status != 0)
release(h);
return next;
}
// 如果已經上鎖(a != 0),並且是讀鎖,那麼立刻返回 stamp。當前是讀鎖,不需要做處理
else if (a != 0L && a < WBIT)
return stamp;
else
break;
}
return 0L;
}
/**
* 轉成樂觀讀鎖。
* 版本號正確並且當前是樂觀讀,返回最新的版本號
* 如果是當前線程獲取的寫鎖,釋放寫鎖,如果有等待隊列有後續節點,那麼喚醒。最後返回最新的版本號
* 如果是當前線程獲取的讀鎖,那麼釋放讀鎖並返回最新的版本號。
*
* @param stamp a stamp
* @return a valid optimistic read stamp, or zero on failure
*/
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;
}
/**
* 釋放寫鎖
* 如果有寫鎖,那麼釋放寫鎖。查看是否有需要喚醒後續節點。如果釋放寫鎖成功返回 true,否則返回 false
*
* @return {@code true} if the lock was held, else false
*/
public boolean tryUnlockWrite() {
long s; WNode h;
if (((s = state) & WBIT) != 0L) {
state = (s += WBIT) == 0L ? ORIGIN : s;
if ((h = whead) != null && h.status != 0)
release(h);
return true;
}
return false;
}
/**
* 釋放讀鎖
* 如果有讀鎖,那麼釋放讀鎖,查看是否有需要喚醒後續節點。如果釋放讀鎖成功返回 true,否則返回 false
*
* @return {@code true} if the read lock was held, else false
*/
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;
}
// status monitoring methods
/**
* 返回當前讀鎖的數量
*/
private int getReadLockCount(long s) {
long readers;
if ((readers = s & RBITS) >= RFULL)
readers = RFULL + readerOverflow;
return (int) readers;
}
/**
* 返回當前是否有線程持有寫鎖
*
* @return {@code true} if the lock is currently held exclusively
*/
public boolean isWriteLocked() {
return (state & WBIT) != 0L;
}
/**
* 返回當前是否有讀鎖
*
* @return {@code true} if the lock is currently held non-exclusively
*/
public boolean isReadLocked() {
return (state & RBITS) != 0L;
}
/**
* 返回當前讀鎖的數量
* @return the number of read locks held
*/
public int getReadLockCount() {
return getReadLockCount(state);
}
/**
* Returns a string identifying this lock, as well as its lock
* state. The state, in brackets, includes the String {@code
* "Unlocked"} or the String {@code "Write-locked"} or the String
* {@code "Read-locks:"} followed by the current number of
* read-locks held.
*
* @return a string identifying this lock, as well as its lock state
*/
public String toString() {
long s = state;
return super.toString() +
((s & ABITS) == 0L ? "[Unlocked]" :
(s & WBIT) != 0L ? "[Write-locked]" :
"[Read-locks:" + getReadLockCount(s) + "]");
}
// views
/**
* 返回一個讀鎖的視圖。
* 利用 StampedLock 的讀鎖來實現 Lock 接口定義的功能。由於本身並不會處理線程中斷,所以無法實現 lockInterruptibly。
*
* @return the lock
*/
public Lock asReadLock() {
ReadLockView v;
return ((v = readLockView) != null ? v :
(readLockView = new ReadLockView()));
}
/**
* 返回一個寫鎖的試圖。
* 利用 StampedLock 的寫鎖來實現 Lock 接口定義的功能。由於本身並不會處理線程中斷,所以無法實現 lockInterruptibly。
*
* @return the lock
*/
public Lock asWriteLock() {
WriteLockView v;
return ((v = writeLockView) != null ? v :
(writeLockView = new WriteLockView()));
}
/**
* 返回一個讀寫鎖的試圖
* 利用讀鎖視圖和寫鎖視圖實現讀寫鎖
*
* @return the lock
*/
public ReadWriteLock asReadWriteLock() {
ReadWriteLockView v;
return ((v = readWriteLockView) != null ? v :
(readWriteLockView = new ReadWriteLockView()));
}
final class ReadLockView implements Lock {
public void lock() { readLock(); }
public void lockInterruptibly() throws InterruptedException {
readLockInterruptibly();
}
public boolean tryLock() { return tryReadLock() != 0L; }
public boolean tryLock(long time, TimeUnit unit)
throws InterruptedException {
return tryReadLock(time, unit) != 0L;
}
public void unlock() { unstampedUnlockRead(); }
public Condition newCondition() {
throw new UnsupportedOperationException();
}
}
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();
}
}
final class ReadWriteLockView implements ReadWriteLock {
public Lock readLock() { return asReadLock(); }
public Lock writeLock() { return asWriteLock(); }
}
// Unlock methods without stamp argument checks for view classes.
// Needed because view-class lock methods throw away stamps.
final void unstampedUnlockWrite() {
WNode h; long s;
if (((s = state) & WBIT) == 0L)
throw new IllegalMonitorStateException();
state = (s += WBIT) == 0L ? ORIGIN : s;
if ((h = whead) != null && h.status != 0)
release(h);
}
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;
}
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
state = ORIGIN; // reset to unlocked state
}
// internals
/**
* 處理超過讀鎖上限後溢出部分的存儲
*
* @param s a reader overflow stamp: (s & ABITS) >= RFULL
* @return new stamp on success, else zero
*/
private long tryIncReaderOverflow(long s) {
/**
* (s & ABITS) == RFULL 表明未上寫鎖且讀鎖達到上限
*/
if ((s & ABITS) == RFULL) {
// 更新成成功則利用 readerOverflow 存儲溢出部分的讀鎖次數
// 溢出部分的版本號都是 RFULL
if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) {
++readerOverflow;
state = s;
return s;
}
}
// 很可能是其他線程獲取了寫鎖。此時將 CPU 的使用權讓出去
else if ((LockSupport.nextSecondarySeed() &
OVERFLOW_YIELD_RATE) == 0)
Thread.yield();
return 0L;
}
/**
* 超過讀鎖上限時,減少讀鎖數量需要通過調用 tryDecReaderOverflow 來減少讀鎖數量
*
* @param 版本號
* @return 新版本號或者釋放失敗返回 0
*/
private long tryDecReaderOverflow(long s) {
//(s & ABITS) == RFULL 表示讀鎖超過上限
if ((s & ABITS) == RFULL) {
// s | RBITS 確保是鎖溢出的狀態
if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) {
int r; long next;
// 如果 readerOverflow > 0 則從 readerOverflow 中扣除鎖的次數
if ((r = readerOverflow) > 0) {
readerOverflow = r - 1;
next = s;
}
// 如果 readerOverflow == 0 則從 state 中扣除鎖的次數
else
next = s - RUNIT;
state = next;
// 返回新版本號
return next;
}
}
// 到這裏可能是上了寫鎖,那麼讓渡出 CPU 的權限
else if ((LockSupport.nextSecondarySeed() &
OVERFLOW_YIELD_RATE) == 0)
Thread.yield();
return 0L;
}
/**
* 喚醒指定節點的後續非取消節點。指定的節點通常是等待隊列第一個節點,而被喚醒的節點通常是 h.next。
* 但是如果 h.next 的指向被其他線程修改導致 h.next 發生變化(通常是取消節點引起的),
* 那麼需要從隊尾往前回溯非取消狀態的節點。release 方法不一定能夠成功喚醒後續節點,
* 但是取消節點操作會有額外的釋放後續節點的操作確保節點能夠成功被喚醒
*/
private void release(WNode h) {
if (h != null) {
WNode q; Thread w;
// 將指定節點的狀態更新成初始狀態
U.compareAndSwapInt(h, WSTATUS, WAITING, 0);
// 如果 h.next 是空或者是取消狀態,那麼從隊尾回溯一個非取消狀態的節點
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;
}
// 如果 q 非空且綁定了線程,喚醒線程
if (q != null && (w = q.thread) != null)
U.unpark(w);
}
}
/**
* 嘗試獲取鎖並返回最新版本號
*
* @param interruptible 是否需要響應中斷
* @param deadline if nonzero, the System.nanoTime value to timeout
* at (and return zero)
* @return 最新版本號或者中斷標誌
*/
private long acquireWrite(boolean interruptible, long deadline) {
WNode node = null, p;
/**
* 檢查隊列是否爲空,如果爲空則初始化隊列
* 由於是 if else,所以每次 for 循環都只處理一小段代碼。越往後需要循環越多的次數才能到達
*/
for (int spins = -1;;) { // spin while enqueuing
long m, s, ns;
//如果是初始化狀態並且 CAS 更新鎖成功,那麼返回最新的版本號。初始狀態是 1 1000 0000
if ((m = (s = state) & ABITS) == 0L) {
if (U.compareAndSwapLong(this, STATE, s, ns = s + WBIT))
return ns;
}
// 如果是第一次進來(spins 一開始就賦值爲-1),那麼計算自旋次數。如果當前狀態只有寫鎖上有值,
// 那麼自選次數爲入隊前的最大自旋次數,否則自選次數爲 0
else if (spins < 0)
spins = (m == WBIT && wtail == whead) ? SPINS : 0;
// 如果計算得到的自旋次數大於 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;
}
//初始化 node 節點
else if (node == null)
node = new WNode(WMODE, p);
// 將 node 節點的上一個節點更新成隊尾節點
else if (node.prev != p)
node.prev = p;
//將 node 節點添加到隊列尾部。
else if (U.compareAndSwapObject(this, WTAIL, p, node)) {
p.next = node;
break;
}
}
/**
* 循環等待獲取鎖或者阻塞
*/
for (int spins = -1;;) {
// h 是第一個節點,np 是隊尾節點 pp 是倒數第二個節點,ps 表示是隊尾節點的狀態
WNode h, np, pp; int ps;
//如果是隊列第一個節點,那麼自旋等待所釋放。
if ((h = whead) == p) {
// 設置自旋次數
if (spins < 0)
spins = HEAD_SPINS;
//如果上一次自旋(第一次是 HEAD_SPINS 次)還未獲取到鎖,那麼只要未超過 MAX_HEAD_SPINS 次,在當前自旋數量上增加 2 倍數量
else if (spins < MAX_HEAD_SPINS)
spins <<= 1;
// 自旋修改 state,做上鎖操作
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;
}
}
// h != null 表示隊列中已經有節點
else if (h != null) { // help release stale waiters
WNode c; Thread w;
// cowait 不爲 null 表示是第一個節點是讀鎖,並且有多個線程獲取了讀鎖。遍歷 cowait 隊列並喚醒所有的線程
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
}
// CAS 將隊尾的狀態變更成-1。如此競爭隊尾時,無法同時競爭成功。
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;
/**
* 1. 隊尾節點處於等待轉改
* 2. 隊尾節點不是隊列第一個節點結點 或者 隊列已經上鎖
* 3. 隊列第一個節點和隊尾節點在自旋期間未發生變化
* 同時滿足以上三個條件,當前線程被掛起
*/
if (p.status < 0 && (p != h || (state & ABITS) != 0L) &&
whead == h && node.prev == p)
U.park(false, time); // emulate LockSupport.park
// 線程被喚醒後將線程的 parkBlocker 屬性置空
node.thread = null;
U.putObject(wt, PARKBLOCKER, null);
// 如果是線程中斷導致的喚醒,那麼取消節點。釋放鎖方式喚醒線程會將節點剔出隊列。
if (interruptible && Thread.interrupted())
return cancelWaiter(node, node, true);
}
}
}
}
/**
* 獲取讀鎖
* 1. 如果隊列中就一個節點,那麼通過自旋+CAS 修改的方式獲取鎖
* 1. CAS 是否修改成功,如果修改成功則返回版本號
* 2. 如果有必要則初始化隊列
* 3.
*
* @param interruptible 是否響應中斷 true 表示響應中斷
* @param deadline 超時時間
* @return 版本號或者中斷標誌(-1 表示線程被中斷)
*/
private long acquireRead(boolean interruptible, long deadline) {
WNode node = null, p;
for (int spins = -1;;) {
WNode h;
// 如果隊列中就一個節點,那麼通過自旋+CAS 修改的方式獲取鎖
if ((h = whead) == (p = wtail)) {
for (long m, s, ns;;) {
// 如果未上寫鎖,則直接 CAS 修改,否則使用 tryIncReaderOverflow 修改
// 如果修改成功則返回版本號
if ((m = (s = state) & ABITS) < RFULL ?
U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
(m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L))
return ns;
// 如果上寫鎖了,那麼自旋直到 CAS 修改成功
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;
}
}
// 表示已經存在讀節點,新增的讀節點添加到已經存在讀節點的 cowait 隊列上
else if (!U.compareAndSwapObject(p, WCOWAIT,
node.cowait = p.cowait, node))
node.cowait = null;
else {
for (;;) {
WNode pp, c; Thread w;
// 如果頭節點不爲空且頭節點 cowait 隊列不爲空,那麼喚整個 cowait 隊列(頭節點的線程是當前持有鎖的線程,應該都在運行中。)
if ((h = whead) != null && (c = h.cowait) != null &&
U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
(w = c.thread) != null) // help release
U.unpark(w);
/**
* h == (pp = p.prev) 頭結點是當前節點的前一個節點的前一個節點(快到自己了)
* h == p 頭結點是當前節點的前一個節點(快到自己了)
* pp == null 當前節點的前一個節點的前一個節點是空(快到自己了)
* 只要不上寫鎖,當前線程就不斷自旋修改版本號以便快速獲取鎖
*/
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);
}
/**
* 頭結點未發生變化
* 並且指定節點的前一個節點的指向和 pp 屬性指向一致
*/
if (whead == h && p.prev == pp) {
long time;
/**
* pp = null(前面第二個元素時 null 表示快到自己了)
* h == p 前一個節點是頭結點表示快到自己了
* p.status > 0 表示前一個節點已經取消等待,可能快到自己了
* 如果快到自己了,跳出當前循環,重新進行不斷嘗試獲取鎖的自旋
*/
if (pp == null || h == p || p.status > 0) {
node = null;
break;
}
// 如果等待的時間已經到了,那麼取消等待(調用 cancelWaiter)
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;
/**
* h != pp 頭結點不是指定節點前的第二個節點
* (state & ABITS) == WBIT 有個其他線程獲取到了寫鎖
* whead == h 頭節點未發生變化
* p.prev == pp 前一個節點和前前節點的指向未發生變化
* 如果沒有快到當前線程(隊列上距離當前線程還有 2 個以上的節點),並且有其他線程獲取了寫鎖那麼阻塞隊列
*/
if ((h != pp || (state & ABITS) == WBIT) &&
whead == h && p.prev == pp)
U.park(false, time);
// 以下代碼會在線程喚醒之後執行
// park 方式喚醒的線程可能是等待時間到了,或者線程被中斷了,或者 unpark 方法執行了
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;
/**
* (m = (s = state) & ABITS) < RFULL 表示未上寫鎖,並且讀鎖未溢出
* CAS 加上一個讀鎖 如果成功則返回版本號。
* 成功後協助喚醒頭結點的 cowait 隊列
* 失敗則自旋
*/
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;
}
}
// 頭結點不爲空,那麼協助喚醒頭結點的 cowait 隊列
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);
}
}
}
}
/**
* 取消指定的節點,並將其從等待隊列中刪除。刪除 cowait 隊列中取消了的節點,並喚醒剩餘 cowait 隊列上的節點。檢查是否有必要喚醒等待隊列中的第一個等待節點(非第一個節點,非取消狀態的節點)
*
* @param node if nonnull, the waiter 等待節點
* @param group either node or the group node is cowaiting with 縱向隊列上的等待節點
* @param interrupted if already interrupted 是否響應中斷
* @return INTERRUPTED if interrupted or Thread.interrupted, else zero
*/
private long cancelWaiter(WNode node, WNode group, boolean interrupted) {
// 節點都不能爲空
if (node != null && group != null) {
Thread w;
node.status = CANCELLED;
//將取消的節點從縱向(cowait)隊列中剔除
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;
}
// group 和 node 是一個節點。
if (group == node) {
// 喚醒縱向隊列(cowait)未取消的節點
for (WNode r = group.cowait; r != null; r = r.cowait) {
if ((w = r.thread) != null)
U.unpark(w); // wake up uncancelled co-waiters
}
// 由於當前節點需要剔出隊列,所以需要將當前節點的前一個未取消節點和後一個未取消節點重新綁定。綁定操作類似:node.prev.next = node.next;node.next.prev = node.pre;
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);
//喚醒當前節點下一個未取消節點(cowait 隊列上的節點)
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;
while ((h = whead) != null) {
// q 是隊列中非第一個節點並且處於等待狀態或者初始狀態的節點
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) {
// 如果 q 節點存在並且 h 的狀態爲初始化狀態,並且當前不是寫鎖,並且 q 是讀線程
// 滿足以上條件則喚醒第一個等待節點(頭節點的後續未取消的節點)
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;
}
// Unsafe mechanics
private static final sun.misc.Unsafe U;
private static final long STATE;
private static final long WHEAD;
private static final long WTAIL;
private static final long WNEXT;
private static final long WSTATUS;
private static final long WCOWAIT;
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);
}
}