4.1 NonfairSync - 非公平鎖
- Sync 對象的非公平鎖
-
教面試官ReentrantLock源碼(下)5 nonfairTryAcquire6 tryLock7 tryRelease
4.1.1 lock
- 非公平模式的 lock 方法
-
教面試官ReentrantLock源碼(下)5 nonfairTryAcquire6 tryLock7 tryRelease - 若 CAS(已經定義并實作在 AQS 中的 final 方法)state 成功,即擷取鎖成功并将目前線程設定為獨占線程
若 CAS state 失敗,即擷取鎖失敗,則進入 AQS 中已經定義并實作的 Acquire 方法善後
這裡的 lock 方法并沒有直接調用 AQS 提供的 acquire 方法,而是先試探地使用 CAS 擷取了一下鎖,CAS 操作失敗再調用 acquire 方法。這樣設計可以提升性能。因為可能很多時候我們能在第一次試探擷取時成功,而不需要再經過 acquire => tryAcquire => nonfairAcquire 的調用鍊。
4.1.2 tryAcquire
其中真正的實作 nonfairTryAcquire 就定義在其父類 Sync 中。下一節分析。
4.2 FairSync - 公平鎖
隻實作 lock 和 tryAcquire 兩個方法
4.2.1 lock
lock 方法加鎖成功,直接傳回,是以可以繼續執行業務邏輯。
- 公平模式的 lock
-
教面試官ReentrantLock源碼(下)5 nonfairTryAcquire6 tryLock7 tryRelease - 直接調用 acquire,而沒有像非公平模式先試圖擷取,因為這樣可能導緻違反“公平”的語義:在已等待在隊列中的線程之前擷取了鎖。
acquire 是 AQS 的方法,表示先嘗試獲得鎖,失敗之後進入同步隊列阻塞等待。
4.2.2 tryAcquire
- 該方法是 AQS 在 acquire 方法中留給子類去具體實作的
-
教面試官ReentrantLock源碼(下)5 nonfairTryAcquire6 tryLock7 tryRelease
公平模式
不要授予通路權限,除非遞歸調用或沒有等待線程或是第一個調用的。
protected final boolean tryAcquire(int acquires) {
// 擷取目前的線程
final Thread current = Thread.currentThread();
// 擷取 state 鎖的狀态(volatile 讀語義)
int c = getState();
// state == 0 => 尚無線程擷取鎖
if (c == 0) {
// 判斷 AQS 的同步對列裡是否有線程等待
if (!hasQueuedPredecessors() &&
// 若沒有則直接 CAS(保證原子性,線程安全) 擷取鎖
compareAndSetState(0, acquires)) {
// 擷取鎖成功,設定獨占線程
setExclusiveOwnerThread(current);
return true;
}
}
// 已經擷取鎖的是否為目前的線程?
else if (current == getExclusiveOwnerThread()) {
// 鎖的重入, 即 state 加 1
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
// 已經擷取 lock,是以這裡不考慮并發
setState(nextc);
return true;
}
return false;
}
和Sync#nonfairTryAcquire類似,唯一不同的是當發現鎖未被占用時,使用 hasQueuedPredecessors 確定了公平性。
hasQueuedPredecessors
判斷目前線程是不是屬于同步隊列的頭節點的下一個節點(頭節點是釋放鎖的節點):
- 是(傳回false),符合FIFO,可以獲得鎖
- 不是(傳回true),則繼續等待
public final boolean hasQueuedPredecessors() {
// 這種方法的正确性取決于頭在尾之前初始化和頭初始化。如果目前線程是隊列中的第一個線程,則next是精确的
Node t = tail; // 按反初始化順序讀取字段
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
5 nonfairTryAcquire
執行非公平的 tryLock。 tryAcquire 是在子類中實作的,但是都需要對trylock 方法進行非公平的嘗試。
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)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
無參的 tryLock 調用的就是此方法
6 tryLock
6.1 無參
Lock 接口中定義的方法。
- 僅當鎖在調用時未被其他線程持有時,才擷取鎖
-
教面試官ReentrantLock源碼(下)5 nonfairTryAcquire6 tryLock7 tryRelease - 如果鎖未被其他線程持有,則擷取鎖,并立即傳回值 true,将鎖持有計數設定為1。即使這個鎖被設定為使用公平的排序政策,如果鎖可用,調用 tryLock() 也會立即獲得鎖,不管其他線程是否正在等待鎖。這種妥協行為在某些情況下是有用的,雖然它破壞了公平。如果想為這個鎖執行公平設定,那麼使用 tryLock(0, TimeUnit.SECONDS),這幾乎是等價的(它還可以檢測到中斷)。
如果目前線程已經持有該鎖,那麼持有計數将增加1,方法傳回true。
如果鎖被另一個線程持有,那麼這個方法将立即傳回值false。
6.2 有參
- 提供了逾時時間的入參,在時間内,仍沒有得到鎖,會傳回 false
-
教面試官ReentrantLock源碼(下)5 nonfairTryAcquire6 tryLock7 tryRelease - 其中的 doAcquireNanos 已經實作好在 AQS 中。
7 tryRelease
釋放鎖,對于公平和非公平鎖都适用
protected final boolean tryRelease(int releases) {
// 釋放 releases (由于可重入,這裡的 c 不一定直接為 0)
int c = getState() - releases;
// 判斷目前線程是否是擷取獨占鎖的線程
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 鎖已被完全釋放
if (c == 0) {
free = true;
// 無線程持有獨占鎖,是以置 null
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}