bilibili-Java併發學習筆記14 AQS 之 ReentrantLock
基於 java 1.8.0
P46_可重入鎖對於AQS的實現源碼分析
ReentrantLock 使用案例
Lock lock = new ReentrantLock();
lock.lock();
try {
// do something
} finally {
lock.unlock();
}
- 公平鎖和非公平鎖
// 默認非公平鎖
Lock lock = new ReentrantLock();
// 公平鎖
Lock lock = new ReentrantLock(true);
// 非公平鎖
Lock lock = new ReentrantLock(false);
/**
* 底層實現
*/
private final Sync sync;
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
/**
* Sync object for fair locks
*/
static final class FairSync extends Sync {...}
/**
* Sync object for non-fair locks
*/
static final class NonfairSync extends Sync {...}
/**
* Base of synchronization control for this lock. Subclassed
* into fair and nonfair versions below. Uses AQS state to
* represent the number of holds on the lock.
*/
abstract static class Sync extends AbstractQueuedSynchronizer {...}
- 非公平鎖實現 lock() 上鎖實現
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
// cas 操作,若 state 是 0,則將其改爲 1,並將獨佔鎖的線程標記指向自己
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
// 若互斥鎖已被別的線程佔有,則
else
// AQS 的 acquire(int) 方法
acquire(1);
}
/**
* 以獨佔模式獲取,忽略中斷。通過調用至少一次 tryAcquire 在成功時返回來實現。
* 否則線程將排隊,可能會反覆阻塞和取消阻塞,調用 tryAcquire 直到成功。
* 此方法可用於實現方法 Lock.lock()
*
* @param arg the acquire argument. 這個值被傳遞到tryAcquire,但在其他方面是不令人驚訝的,可以表示任何您喜歡的。
*/
public final void acquire(int arg) {
// tryAcquire 嘗試獲取鎖
// addWaiter 將線程加入等待隊列以獨佔模式
// acquireQueued 查看當前線程是否被中斷
if ( !tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg) )
selfInterrupt();
}
// NonfairSync in ReentrantLock 實現
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
/**
* Performs non-fair tryLock.
* tryAcquire是在子類中實現的,但這兩個類都需要trylock方法的非空嘗試。
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// state 爲 0 表示當前無線程佔有鎖
// 通過 CAS 操作去獲取鎖
if (compareAndSetState(0, acquires)) {
// 當前佔有鎖的線程標記爲自己
setExclusiveOwnerThread(current);
return true;
}
}
// 如果當前線程已被佔有,且佔有的線程正是自己
// 重新進入這個鎖
// 所以說 ReentrantLock 是可重入鎖
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
// 不過 state 值 +1
setState(nextc);
return true;
}
return false;
}
/**
* 爲當前線程和給定模式創建並排隊節點。
*
* @param mode Node 模式 : Node.EXCLUSIVE 獨佔鎖, Node.SHARED 共享鎖
* @return the new node
*/
private Node addWaiter(Node mode) {
// 創建隊列節點
Node node = new Node(Thread.currentThread(), mode);
// 嘗試enq的快速路徑;失敗時備份到完整enq
// 將新節點加入隊尾
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
/**
* 以獨佔不間斷模式獲取隊列中已存在的線程。由條件等待方法和獲取使用。
*
* @param node the node
* @param arg the acquire argument
* @return 如果在等待時中斷返回 true
*/
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
- 公平鎖實現 lock() 上鎖實現
final void lock() {
// 請求鎖
acquire(1);
}
public final void acquire(int arg) {
// 嘗試獲取獨佔鎖
// 若獲取失敗加入等待隊列
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
/**
* 公平鎖版本的 tryAcquire.
* 除非遞歸調用或沒有等待者或是第一個,否則不要授予訪問權限。
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 無線程佔有鎖
if (c == 0) {
// hasQueuedPredecessors 判斷等待隊列中有沒有其他等待線程
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;
}
/**
* 查詢是否有任何線程等待獲取的時間比當前線程長。
*
* 調用此方法相當於(但可能比)更有效:
* getFirstQueuedThread() != Thread.currentThread() && hasQueuedThreads()}
*
* 請注意,由於中斷和超時可能會在任何時候發生取消,
* 所以一個真正的返回並不保證其他線程會在當前線程之前獲取。
* 同樣,由於隊列爲空,因此在該方法返回false之後,另一個線程也有可能贏得排隊競爭。
*
* 這種方法被設計用於公平同步器,以避免碰撞。
* 這種同步器的 tryAcquire 方法應該返回 false,如果這個方法返回 true(除非這是可重入的獲取),
* 那麼它的 tryAcquireShared 方法應該返回一個負值。
* 例如,公平、可重入、獨佔模式同步器的 tryAcquire 方法可能如下所示:
*
* protected boolean tryAcquire(int arg) {
* if (isHeldExclusively()) {
* // A reentrant acquire; increment hold count
* return true;
* } else if (hasQueuedPredecessors()) {
* return false;
* } else {
* // try to acquire normally
* }
* }}
*
* @return 如果在當前線程之前有一個排隊線程,則爲true;如果當前線程位於隊列的頭部或隊列爲空,則爲 false
*
* @since 1.7
*/
public final boolean hasQueuedPredecessors() {
// 它的正確性取決於head在tail之前被初始化頭。下一個如果當前線程是隊列中的第一個線程,則爲精確的。
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
- 釋放鎖
public void unlock() {
sync.release(1);
}
/**
* 以獨佔模式釋放。如果 tryRelease 返回true,則通過取消阻止一個或多個線程來實現。此方法可用於實現方法 Lock#unlock
*
*
*
* @param arg 釋放參數。這個值被傳遞到tryRelease,但在其他方面是不令人驚訝的,可以表示任何您喜歡的。
* @return 從tryRelease返回的值
*/
public final boolean release(int arg) {
// 若鎖完全釋放(state爲0)
if (tryRelease(arg)) {
// 喚醒節點的後續節點
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
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);
}
// 重入鎖層次 -1
setState(c);
return free;
}
/**
* 喚醒節點的後續節點(如果存在)。
*
* @param node the node
*/
private void unparkSuccessor(Node node) {
/*
* 如果狀態爲陰性(即可能需要信號),則嘗試清除,以等待信號。如果失敗或狀態被等待線程更改,則正常。
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* unpark的線程被保存在後續節點中,後者通常只是下一個節點。但如果取消或明顯爲空,則從尾部向後遍歷,以找到實際未取消的後繼項。
*/
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}