Reentrant 2 前兩篇寫完了後我自己研究了下,還有有很多疑惑和問題,這篇就繼續以自問自答的方式寫 如果沒看過第1篇的可以先看看那個https://www.cnblogs.com/sunankang/p/16458795.html public final void acquire(int a ...
Reentrant 2
前兩篇寫完了後我自己研究了下,還有有很多疑惑和問題,這篇就繼續以自問自答的方式寫
如果沒看過第1篇的可以先看看那個https://www.cnblogs.com/sunankang/p/16458795.html
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
進入acquireQueued
方法
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);
}
}
第一個問題
interrupted這個變數的作用
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
在parkAndCheckInterrupt
方法中最後return的是這個線程是否被打斷,它的作用是啥?
先來回顧interrupt()
,interrupted()
和isInterrupted()
三者區別,長得很像,註意區分
interrupt()
的作用是中斷線程,如果被中斷的線程處於阻塞狀態下,例如調用wait()
,join()
sleep()
,則拋出異常,否則只是設置一個中斷標記為true,註意:僅僅是設置中斷狀態為true,並不會去 "中斷" 線程
interrupted()
獲取線程的中斷狀態並且清空中斷狀態(將中斷狀態設置為false)
isInterrupted()
獲取線程的中斷狀態並不會清除中斷狀態
調用 interrupt 會使park方法立即結束,可以理解為喚醒
繼續代碼,看這個變數最後到了哪裡
情況1 沒有被打斷過
假設線程沒有被中斷過,那麼parkAndCheckInterrupt
返回就是false
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);
}
}
那麼不進入 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
這個if,獲取到鎖後返回false,回到acquire
方法
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
因為false,所以不進入selfInterrupt()
,方法結束
情況2 park或準備park,被喚醒後直接獲取到了鎖
先證明一下打斷是會喚醒park中的線程的
我就再重覆粘一下代碼了,方便看
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
那麼返回的就是true,回到上級acquireQueued
方法
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);
}
}
因為返回true,所以進入if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
將interrupted返回true
假設迴圈獲取到鎖,那麼再返回上一級acquire()
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
那麼進入selfInterrupt()
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
是不是有點疑惑?我如果沒有調用過interrupt()
那ReentrantLock就不做任何操作,我如果調用了,那它再給我調用一次 ???? 還有情況3
情況3 park或準備park,被喚醒後沒有獲取到鎖
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;
}
//假設在調用shouldParkAfterFailedAcquire成功後,馬上就要調用parkAndCheckInterrupt 時間片用完了
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
那麼這個時候interrupted屬性就有用了
首先要知道一點,一個被中斷的線程是無法park的,除非清除了中斷狀態,即設置為將中斷狀態設置為false, 口說無憑,直接上圖
第二張圖還是在park狀態,證明瞭被打斷的線程是無法park的,除非將它中斷狀態設置為false
那麼回到代碼中就能知道這個的作用
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);
}
}
如果線程被打斷喚醒,還是在for(;;)
中,還是去獲取鎖,假設沒有獲取到呢?那麼就一直在for迴圈中嘎嘎跑,因為線程的狀態是被中斷的,無法再次park了
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
那麼現在懂了最後的Thread.interrupted()
作用了嗎,就是將中斷狀態設置回false,好讓線程沒有獲取到鎖繼續park
那這時候可能就問了:那你ReentrantLock把中斷狀態給我清空了,我自己如果有需要根據中斷狀態來判斷的代碼咋辦啊?
好,咱們從park先被打斷來捋一下
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
因為被打斷,線程醒來,執行Thread.interrupted()
並清空中斷狀態,返回true
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);
}
}
因為返回的是true,所以進入if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
的代碼塊,將interrupted
屬性設置為true
那麼for(;;)
迴圈再來一次,如果沒有獲取到鎖.繼續park,直到被喚醒,走tryAcquire()
獲取到為止,那麼此時interrupted
變數就為true了
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
那麼退出acquireQueued()
方法回到acquire()
中,因為acquireQueued()
返回的是true,所以進入selfInterrupt()
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
所以懂了嗎?