ReentrantLock原始碼之公平鎖與非公平鎖
ReentrantLock類與Synchronized關鍵字的主要區別之一就是可以實現公平鎖和非公平鎖。我們看下ReentrantLock類是如果實現公平鎖與非公平鎖的。
1、非公平鎖的實現
ReentrantLock預設建構函式為非公平鎖(為什麼?因為非公平鎖的實現可以減少執行緒的切換,提高執行效率。)
public ReentrantLock() {
sync = new NonfairSync();
}
ReentrantLock中含有一個內部類NonfairSync,如下
static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; /** * 非公平lock方法。首先,直接呼叫AQS的compareAndSetState方法,原子設定同步狀態的值。 * True,表明獲取鎖成功,設定AQS的成員變數獨佔鎖的擁有者exclusiveOwnerThread為當前 * 執行緒 * False,獲取鎖失敗。呼叫AQS的acquire方法。 */ final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } //實現父類的tryAcquire方法。嘗試以獨佔模式獲取同步狀態。 protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } }
ReentrantLock在呼叫lock方法時,其實呼叫的是NonfairSync 類的lock方法,此方法先呼叫AQS的模板方法compareAndSetState來原子的設定同步狀態的值,如果成功,則表明獲取鎖成功,失敗,則呼叫AQS的模板方法acquire()。呼叫AQS的模板方法acquire()如下
//該方法呼叫NonfairSync類中重寫的tryAcquire方法 public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt();// 如果沒有獲取到同步狀態,並且最終執行緒的中斷狀態為true,則會呼叫selfInterrupt方法進行執行緒中斷。 }
acquire方法是AQS實現的模板方法。tryAcquire方法需要具體的子類進行實現,這個方法一會再看。先看兩個方法addWaiter()和acquireQueued() 此兩個方法均為AQS的模板方法,分別如下
//addWaiter方法是AQS的模板方法,實現建立節點並進入同步佇列。 private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode); // Try the fast path of enq; backup to full enq on failure Node pred = tail; if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } enq(node); return node; }
//acquireQueued是AQS的模板方法,當前執行緒會根據公平原則來進行阻塞等待,直到獲取到同步狀態位置。並且會返回在阻塞過程中執行緒是否被中斷過。
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);
}
}
現在來看AQS的子孫類NonfairSync實現的tryAcquire方法
//實現父類的tryAcquire方法。嘗試以獨佔模式獲取同步狀態。
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
nonfairTryAcquire()方法在AQS的子類Sync類中已實現,如下。(為什麼放到Sync類中?)
//Sync實現的nonfairTryAcquire方法。
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {//同步狀態為0,則再次設定同步狀態,成功,則返回True
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {//同步狀態不為0,當佔用鎖的執行緒為當前執行緒時,重入,返回成功。
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
2、公平鎖的實現
公平鎖呼叫Lock方法時,並沒有上來呼叫compareAndSetState方法(因為不知道同步佇列中是否有執行緒等待)。首先呼叫AQS的模板方法acquire方法,addWaiter方法與acquireQueued呼叫的邏輯相同,主要看公平鎖版本tryAcquire方法的實現。
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
//注意這裡與公平鎖實現的不同,公平鎖不能直接呼叫CAS方法,因為公平鎖需要確認同步佇列中是否有等待獲取同步狀態的執行緒
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
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;
}
}
我們發現與非公平鎖相比,主要多一句!hasQueuedPredecessors,這是實現公平鎖和非公平鎖的關鍵。hasQueuedPredecessors方法是查詢同步佇列中是否有等待獲取鎖的執行緒,只有返回false時,才會設定同步狀態的值。否則,加入同步佇列阻塞等待獲取鎖。提現公平鎖的特點。
參考文章:
(2)《Java併發程式設計的藝術》第五章Java中的鎖。