ReentrantLock原理及原始碼閱讀
ReentrantLock原理及原始碼閱讀
1、ReentrantLock介紹
ReentrantLock是可重入的獨佔鎖,同時只能有一個執行緒可以獲取到該鎖,其他執行緒獲取該鎖的執行緒將會被阻塞而被放入該鎖的AQS阻塞佇列裡面。
ReentrantLock最終還是使用AQS來實現的,並且根據引數來決定其內部是一個公平還是非公平鎖。
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
abstract static class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = -5179523762034025860L; /** * Performs {@link Lock#lock}. The main reason for subclassing * is to allow fast path for nonfair version. */ abstract void lock(); .... }
其中Sync類直接繼承自AQS,它的子類NonfairSync
和FairSync
分別實現了獲取鎖的公平和非公平策略
2、獲取鎖的方法
void lock()方法
當一個執行緒呼叫該方法時,說明該執行緒希望獲取該鎖,如果鎖當前沒有被其他執行緒佔用,並且當前執行緒之前沒有獲取過該鎖,則當前執行緒會獲取到該鎖,然後設定當前鎖的擁有者為當前執行緒,並設定AQS的狀態值為1,然後直接返回。如果當前執行緒之前已經獲取過該鎖,則這次只是簡單的把AQS的狀態值加1後返回。如果該鎖寂靜被其他執行緒持有,則呼叫該方法的執行緒會被放入AQS佇列後阻塞掛起。
public void lock() { sync.lock(); }
ReentranLock的lock委託給了sync類,根據建立ReentranLock的建構函式選擇sync的實現是非公平還是公平,這個鎖是一個非公平鎖或公平鎖。以下是sync的子類NonfairSync的情況,也就是非公平鎖。
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
// CAS 設定狀態值state
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
// 獲取失敗則呼叫AQS的acquire方法
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
上述程式碼中,預設AQS狀態值state為0,所以此一個呼叫lock的執行緒會通過CAS設定狀態值為1,CAS成功則代表當前執行緒獲取到了鎖,然後通過setExclusiveOwnerThread
設定該鎖的持有者為當前執行緒。
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
如果有其他執行緒呼叫lock方法獲取該鎖,CAS會失敗,然後呼叫AQS的acquire
方法,傳遞的引數為1.
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
AQS並沒有實現tryAcquire
方法,tryAcquire
方法需要子類自己定製化,所以上面的tryAcquire
實際上會呼叫ReentrantLock重寫的tryAcquire方法。
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
// 獲取當前狀態值。
int c = getState();
// 當前狀態值為0則通過CAS獲得鎖
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;
}
// 為拿到鎖返回false,然後該執行緒被放入AQS阻塞佇列。
return false;
}
非公平鎖的體現
:假設執行緒1呼叫lock方法時發現state狀態值不為0,且當前執行緒不是鎖的持有者則返回false,然後當前執行緒被放入AQS阻塞佇列。這時執行緒2也呼叫了lock方法,發現當前狀態值為0了(假設佔有該鎖的其他執行緒釋放了該鎖),所以通過CAS設定獲取到了該鎖。明明是執行緒1先請求獲取的該鎖,然而執行緒2獲取到了鎖,這就是非公平的體現。這裡執行緒2獲取鎖前並沒有檢視當前AQS佇列裡面是否有比自己更早請求該鎖的執行緒,而是使用了搶奪策略。
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 當前state為0.
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;
}
上述程式碼為公平鎖的方法,公平鎖的話只需要看FairSync重寫的tryAcquire方法。
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
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());
}
上面程式碼中,是實現公平性的核心程式碼,如果當前AQS佇列為空或者當前執行緒是AQS的第一個節點則返回false。其中如果ht則說明當前佇列為空,直接返回false。如果h!=t且snull則說明有一個元素將要作為AQS的第一個節點入佇列,那麼返回true,如果h!=t並且s!=null和s.thread !=Thread.currentThread則說明佇列裡面的第一個元素不是當前執行緒,那麼返回true。
3、釋放鎖的方法
void unlock()方法
public void unlock() {
sync.release(1);
}
嘗試釋放鎖,如果當前執行緒持有該鎖,則呼叫該方法會讓該執行緒對該執行緒持有的AQS狀態值減1,如果減去1後當前狀態值為0,則當前執行緒會釋放鎖,否則僅僅只是減1而已。如果當前執行緒沒有持有該鎖而呼叫了該方法則會丟擲異常。
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
// 如果不是鎖的持有者,則丟擲異常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 如果當前可重入次數為0, 則清空鎖持有執行緒
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
// 設定可重入次數為原始值-1
setState(c);
return free;
}