本文緊接上文的AQS原始碼,如果對于ReentrantLock沒有基礎可以先閱讀我的上一篇文章學習ReentrantLock的原始碼
ReentrantLock鎖重入原理
重入加鎖其實就是將AQS的state進行加一操作
然后釋放鎖資源將AQS的state進行減一操作
當state為0時才會徹底的釋放鎖資源
ReentrantLock可打斷原理
在ReentrantLock中可打斷就是在等待鎖的程序中可以被interrupt打斷(需要呼叫lockInterruptibly),lock方法設定了打斷標記,但是只有在執行緒獲得鎖的時候才能知道自己有沒有在阻塞的程序中有沒有被打斷,
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())// 我們這邊會檢查打斷,如果打斷的話返Thread.interrupted()
interrupted = true; // 這里將打斷標記置為true后,繼續進入回圈,直到獲得到鎖回傳標記
}
} finally {
if (failed)
cancelAcquire(node);
}
}
首先我們需要呼叫加鎖的lockInterruptibly()方法
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
可打斷主要原因在如下代碼解釋,用例外代替了回傳標記,讓執行緒可以直接再park的程序中直接結束
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException(); // 再被打斷的時候不會將其標記置為true,而是直接拋出一個例外,打斷當前的等待,
}
} finally {
if (failed)
cancelAcquire(node);
}
}
ReentrantLock條件變數原理
Condition是一個介面,實際上是ReentrantLock的ConditionObject類作為其實作類,
首先我們創建一個condition就會呼叫其構造方法,其實就是產生一個新的conditionObject
final ConditionObject newCondition() {
return new ConditionObject();
}
await()原始碼
接下來,簡單剖析一個原始碼
- 首先我們得知道,ConditionObject中維護著一個等待的雙向鏈表,其實和阻塞鏈表是很相似的,不同在于不需要前驅進行喚醒,然后在ConditionObject中維護頭和尾的參考就是firstWaiter和lastWaiter成員變數,
public final void await() throws InterruptedException {
if (Thread.interrupted()) // 如果被打斷,拋例外
throw new InterruptedException();
Node node = addConditionWaiter(); // 1.向AQS中添加一個等待鏈表的node
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) { // 是否在阻塞鏈表中
LockSupport.park(this); // 不是的話直接進行阻塞起來
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)// 被打斷了就得把它放阻塞鏈表
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE) // 多執行緒的遺留狀態處理
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
- 添加等待node
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters(); // 這個和阻塞佇列相似不過是全部遍歷,清除不在等待佇列上的node
t = lastWaiter;
}
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null) // 添加到waitting尾部
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
signal原始碼
signal喚醒原始碼,簡單介紹就是直接將等待的firstWaiter指向的等待鏈表的第一個進行解除阻塞,然后將其放入阻塞鏈表中,不過多贅述,
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/473448.html
標籤:其他
上一篇:IOS – OpenGL ES 設定影像濾鏡 GPUImageAmatorkaFilter
下一篇:讀取JVM引數的方法分享
