通過下述代碼,來深挖ReentranLock的底層實現。下述代碼使用了ReentrantLock鎖的lock、unlock、condition等常用方法。讓我們一一解析底層實現。
public class TestReentrantLock {
private static final int MAX_CACHE_SIZE = 16;
private Map<String, String> cache = new HashMap<>();
private ReentrantLock lock = new ReentrantLock();
private Condition notFull = lock.newCondition();
private Condition notEmpty = lock.newCondition();
public void put(String key, String value) {
lock.lock();
try {
while (cache.size() == MAX_CACHE_SIZE) {
notFull.await();
}
cache.put(key, value);
notFull.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void remove(String key) {
lock.lock();
try {
while (cache.isEmpty()) {
notEmpty.await();
}
cache.remove(key);
notFull.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
lock.lock()就是申請鎖,底層調用代碼如下:
public void lock() {
sync.lock();
}
其中,sync是ReentrantLock對象中的內部類abstract static class Sync extends AbstractQueuedSynchronizer。能夠看到Sync是一個抽象類,它有兩個子類FairSync(公平鎖)和NonFailSync(非公平鎖)。兩個都實現了lock()方法
final void lock() {
acquire(1);
}
而acquire(1),1表示申請的鎖資源數量,而acquire方法是繼承於AbstractQueuedSynchronizer(AQS),如下:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
獲取鎖:
首先會嘗試獲取鎖,
獲取失敗時將Thread封成Node節點放入雙向隊列中
1如果他的前置節點爲head節點,則再次嘗試獲取鎖
2如果他的前置節點部位head節點,則設置前置節點狀態爲SIGNAL(表示需要前置節點喚醒),設置成功則阻塞,否則,循環處理12
當上述12異常退出時,若node爲head節點,喚醒後繼節點
大概寫寫,後續補個詳細的流程圖吧~~~
下述爲具體的源碼細節。
acquire(int arg)就是由AQS提供的獲取鎖的框架,其中tryAcquire(arg)就是由子類具體實現獲取鎖的邏輯。
protected final boolean tryAcquire(int acquires) {
//獲取當前線程
final Thread current = Thread.currentThread();
//獲取int state的值,state在不同鎖中代表的含義不一樣。共享鎖中代表共享變量的數量,在獨佔鎖中表示鎖是否被獨佔以及重入次數;
//ReentranLock重入鎖中,等於0時,表示鎖沒有被獨佔;大於等於1時,表示被獨佔且重入的次數。
int c = getState();
if (c == 0) {
//hasQueuedPredecessors()表示AQS隊列中是否存在其他阻塞線程,存在時,獲取鎖失敗。此處也是公平鎖和非公平鎖的主要差別。
// 如果隊列中不存在阻塞線程,通過cas嘗試設置state值爲acuires,同時,通過setExclusiveOwnerThread(thread)將該線程設置爲鎖的獨佔線程
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 通過getExclusiveOwnerThread()判斷嘗試獲取鎖的線程,與已經獲取鎖的線程是否爲同一個。鎖的重入機制
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
//設置state的值爲鎖的最新重入次數。當重入次數減爲0時,才表示鎖沒有被任何線程獨佔
setState(nextc);
return true;
}
return false;
}
private Node addWaiter(Node mode) {
//構造線程節點
Node node = new Node(Thread.currentThread(), mode);
//AQS中的線程阻塞隊列是雙向鏈表支持的,head、tail分別表示頭尾節點
Node pred = tail;
//尾節點不爲null時,嘗試向尾節點處添加node節點
if (pred != null) {
//node節點的前置指針指到尾節點上,然後嘗試,將尾節點設爲node節點
//假設線程a/b都執行到這一步時,會同時將自己的node節點的前置指針指向tail,但是cas保證只會有一個被設置爲尾節點
//如果將這一步放到if語句中,因爲同時有線程釋放鎖且喚醒阻塞線程的情況,因此,會出現阻塞隊列短暫的斷鏈。可以和釋放鎖時的邏輯相互印證
node.prev = pred;
if (compareAndSetTail(pred, node)) {
//將node前置節點的next指針指向node節點,添加成功
pred.next = node;
return node;
}
}
//加入尾節點爲null時,直接入隊
enq(node);
return node;
}
private Node enq(final Node node) {
for (;;) {
Node t = tail;
//加入尾節點爲null時,表示阻塞隊列爲空,通過cas初始化dummy節點
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
//重新向尾節點後添加node節點
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
//返回node節點的前置節點。因爲存在一個dummy頭,因此,加入阻塞隊列只有一個隊列時,t就是head頭節點
return t;
}
}
}
}
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
//for循環一直循環嘗試獲取鎖,當不是head頭時,會park
for (;;) {
//獲取node節點的前置節點,當前置節點爲head節點時,表示阻塞隊列中只有自身
// 然後tryAcquire()嘗試獲取鎖,成功後,將該node節點出隊,返回中斷標誌位位false
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 {
//當上述流程正常退出時,failed=false,說明線程獲取到鎖了,會在release時候喚醒某個線程;
//當異常退出時,需要將該節點置爲CANCELLED狀態,且當是第一個node節點時,喚醒下一個節點
if (failed)
cancelAcquire(node);
}
}
//pred表示node節點的前置節點
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
//waitStatus是將線程封裝的Node節點的狀態,包括CANCELLED(1)/SINGAL(-1)/CONDITION(-2)/PROPAGATE(-3)
//CANCELLED表示該線程因爲超時或中斷而取消獲取鎖,該線程就沒有被阻塞,不需要喚醒等操作
//SINGAL表示節點的後繼節點被阻塞,需要喚醒
//CONDITION表示節點在CONDITION的隊列中,不會出現在AQS的雙端隊列中,當條件滿足時,waitStatus會修改爲0,進入雙端隊列
//PROPAGATE表示傳播,在共享鎖中使用,具體不知道
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
//當node節點的前置節點爲SIGNAL時,表示它可以安全的park(LockSupport提供的park和unpark用於阻塞喚醒線程,底層由unsafe支持)
return true;
//ws>0表示node前置節點是取消狀態
if (ws > 0) {
//do-while用於獲取node前的第一個不是CANCELLED狀態的節點
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
//嘗試將前置節點狀態設爲SIGNAL,注意:雙端隊列中是沒有CONDITION狀態的node節點的,CONDITION狀態的node節點會轉爲0進入雙端隊列
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
private final boolean parkAndCheckInterrupt() {
//使用LockSupport的park阻塞線程
LockSupport.park(this);
//獲取當前線程的中斷狀態,如果被中斷返回true,否則爲false
return Thread.interrupted();
}
private void cancelAcquire(Node node) {
// Ignore if node doesn't exist
if (node == null)
return;
node.thread = null;
//找到不是CANCELLED狀態的前置線程
Node pred = node.prev;
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
Node predNext = pred.next;
//設置node節點爲CANCELLED狀態
node.waitStatus = Node.CANCELLED;
//如果node爲尾節點,則將找到的第一個不爲CANCELLED狀態的前置節點設爲尾節點
if (node == tail && compareAndSetTail(node, pred)) {
//設置pred的next節點爲null,將CANCELLED的節點全部和雙端隊列斷了,gc
compareAndSetNext(pred, predNext, null);
} else {
//如果node不是尾節點。pred爲head說明他是雙端隊列的第一個節點,所以喚醒後繼節點
//如果pred不爲head節點,說明,node節點前後各有線程阻塞在雙端隊列中
int ws;
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null) {
Node next = node.next;
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next);
} else {
unparkSuccessor(node);
}
node.next = node; // help GC
}
}
釋放鎖:
嘗試釋放鎖。釋放成功則喚醒head的後繼節點。釋放失敗的場景是:可重入鎖多次獲取鎖,但只釋放其中一次或幾次等
public void unlock() {
sync.release(1);
}
//由AQS提供的釋放鎖框架
public final boolean release(int arg) {
//嘗試釋放鎖,成功則喚醒第一個節點
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
//獲取state值,即重入的次數,減釋放鎖的次數
int c = getState() - releases;
//如果當前獨佔鎖的線程不是自己,則拋出異常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//當c==0表示獨佔線程將自身所有的鎖都釋放了
if (c == 0) {
free = true;
//將線程獨佔標記爲null
setExclusiveOwnerThread(null);
}
//更新最新的state值
setState(c);
return free;
}