

下面我們借助上面的API圖閱讀源碼
獨占模式獲取鎖acquire,忽略中斷
public final void acquire(int arg) {
// 嘗試獲取鎖,如果獲取不成功,則加入到CLH隊列等待,并且中斷當(dāng)前線程
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
private Node addWaiter(Node mode) {
// 用當(dāng)前線程創(chuàng)建一個Node,mode=Node.EXCLUSIVE也就是null(獨占鎖的nextWaiter是null)
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
// 如果CLH隊尾是空的,或者CAS把新節(jié)點更新成尾結(jié)點失敗了,則調(diào)用完整入隊方法enq
// 否則快速入隊
Node pred = tail;
if (pred != null) {
// 新節(jié)點的前置結(jié)點指向pred(原隊尾結(jié)點)
node.prev = pred;
// 將tail更新成新節(jié)點node,但是不會影響局部變量pred,pred還是原隊尾結(jié)點
if (compareAndSetTail(pred, node)) {
// 原隊尾結(jié)點.next指向新節(jié)點
pred.next = node;
return node;
}
}
// 完整入隊
enq(node);
return node;
}

tryAcquire是需要子類必須重寫的方法,如果不重寫就會拋出UnsupportedOperationException
獨占模式獲取鎖acquire,響應(yīng)中斷
public final void acquireInterruptibly(int arg)
throws InterruptedException {
// 當(dāng)前線程被中斷,拋出InterruptedException
if (Thread.interrupted())
throw new InterruptedException();
// 如果嘗試獲取鎖失敗,那么在可中斷模式下獲取鎖
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
/**
* Acquires in exclusive interruptible mode.
* @param arg the acquire argument
*/
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
// 創(chuàng)建一個和當(dāng)前線程綁定Node,并加入隊尾
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
// 獲取node的前置節(jié)點
final Node p = node.predecessor();
// 如果node的前置節(jié)點就是頭節(jié)點了,那么應(yīng)該拿到資源的線程應(yīng)該是當(dāng)前線程,也就是新創(chuàng)建的node
if (p == head && tryAcquire(arg)) {
// 拿到資源后把node設(shè)置為head節(jié)點,因為這個node拿到資源了,要輪到下一個node了
setHead(node);
// 把原h(huán)ead節(jié)點刪除
p.next = null; // help GC
failed = false;
return;
}
// 如果拿鎖失敗,并且需要掛起,那么需要掛起當(dāng)前線程,并設(shè)置中斷狀態(tài)
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
// 沒拿到鎖取消當(dāng)前節(jié)點拿鎖請求
if (failed)
cancelAcquire(node);
}
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
// 如果前一個節(jié)點的狀態(tài)是SIGNAL,返回true,可以將當(dāng)前線程掛起
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
// 如果如果前一個節(jié)點的狀態(tài)是CANCELLED,那么循環(huán)向前查找取消節(jié)點,把取消節(jié)點從隊列中刪除
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
// 如果是其他狀態(tài),那么將前置節(jié)點的waitStatus設(shè)置成SIGNAL
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
Q:在shouldParkAfterFailedAcquire中為什么前置節(jié)點的狀態(tài)是SIGNAL了,還要掛起當(dāng)前線程?
A:節(jié)點狀態(tài)是SIGNAL表示當(dāng)前節(jié)點如果釋放鎖,可以通知喚醒后面的節(jié)點了。當(dāng)獲取鎖失敗的時候,如果前置節(jié)點狀態(tài)是SIGNAL就表明,可以安全的掛起當(dāng)前線程,因為前置節(jié)點一旦釋放鎖,會通知喚醒后面的節(jié)點。
Q:HEAD節(jié)點是干嘛的,為什要有它?
A:HEAD節(jié)點表示已經(jīng)獲取到鎖的節(jié)點(doAcquireInterruptibly方法,setHead),前面說了,SIGNAL表示當(dāng)前節(jié)點如果釋放鎖,可以通知喚醒后面的節(jié)點了。所以所有的線程在休眠之前都要,把前置節(jié)點的狀態(tài)設(shè)置成SIGNAL,否則自己永遠(yuǎn)無法被喚醒。那么第一個節(jié)點沒有前置節(jié)點怎么辦呢,那就加一個HEAD。
獨占模式獲取鎖tryAcquireNanos(int arg, long nanosTimeout),響應(yīng)中斷并且?guī)в谐瑫r時間
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
// 如果當(dāng)前線程被中斷那么拋出InterruptedException
if (Thread.interrupted())
throw new InterruptedException();
// 直接獲取鎖,若獲取不成功,就進(jìn)入到帶有超時時間的獲取鎖方法doAcquireNanos
return tryAcquire(arg) ||
doAcquireNanos(arg, nanosTimeout);
}
private boolean doAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
// 邊界檢查
if (nanosTimeout <= 0L)
return false;
// 當(dāng)前時間+超時時間=過期時間
final long deadline = System.nanoTime() + nanosTimeout;
// 新加一個節(jié)點
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
// 若前置節(jié)點是head,說明當(dāng)前線程可以獲取鎖了,獲取鎖成功后,釋放原h(huán)ead節(jié)點
// 并且將新加的節(jié)點node設(shè)置為head
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return true;
}
// 循環(huán)拿鎖中超時檢查,若超時返回false
nanosTimeout = deadline - System.nanoTime();
if (nanosTimeout <= 0L)
return false;
// 如果當(dāng)前節(jié)點可以被掛起,并且還剩余的時間大于超時時間的閾值1000L的話,掛起當(dāng)前線程nanosTimeout納秒
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
// 如果當(dāng)前線程被中斷了,那么拋出InterruptedException
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
// 沒獲取到鎖,取消節(jié)點拿鎖請求
if (failed)
cancelAcquire(node);
}
}
Q:為何循壞拿鎖的時候,如果當(dāng)前節(jié)點可以被掛起,并且還剩余的時間大于超時時間的閾值1000L的話,掛起當(dāng)前線程nanosTimeout納秒?
A:為了提高響應(yīng)能力,如果不掛起當(dāng)前線程的話,在這剩余的超時時間里會占用資源一直循環(huán)拿鎖,直到超時或者線程被中斷為止。掛起當(dāng)前線程nanosTimeout納秒,在這期間可以讓別的線程先去占資源。