泥瓦匠聊併發程式設計基礎篇:執行緒中斷和終止
阿新 • • 發佈:2018-12-23
1 執行緒中斷
1.1 什麼是執行緒中斷?
執行緒中斷是執行緒的標誌位屬性。而不是真正終止執行緒,和執行緒的狀態無關。執行緒中斷過程表示一個執行中的執行緒,通過其他執行緒呼叫了該執行緒的 interrupt()
方法,使得該執行緒中斷標誌位屬性改變。
深入思考下,執行緒中斷不是去中斷了執行緒,恰恰是用來通知該執行緒應該被中斷了。具體是一個標誌位屬性,到底該執行緒生命週期是去終止,還是繼續執行,由執行緒根據標誌位屬性自行處理。
1.2 執行緒中斷操作
呼叫執行緒的 interrupt()
方法,根據執行緒不同的狀態會有不同的結果。
下面新建 InterruptedThread 物件,程式碼如下:
/**
* 一直執行的執行緒,中斷狀態為 true
*
* @author Jeff Lee @ bysocket.com
* @since 2018年02月23日19:03:02
*/
public class InterruptedThread implements Runnable {
@Override // 可以省略
public void run() {
// 一直 run
while (true) {
}
}
public static void main(String[] args) throws Exception {
Thread interruptedThread = new Thread(new InterruptedThread(), "InterruptedThread");
interruptedThread.start();
TimeUnit.SECONDS.sleep(2);
interruptedThread.interrupt();
System.out.println("InterruptedThread interrupted is " + interruptedThread.isInterrupted());
TimeUnit.SECONDS.sleep(2 );
}
}
執行 main 函式,結果如下:
InterruptedThread interrupted is true
程式碼詳解:
- 執行緒一直在執行狀態,沒有停止或者阻塞等
- 呼叫了
interrupt()
方法,中斷狀態置為 true,但不會影響執行緒的繼續執行
另一種情況,新建 InterruptedException 物件,程式碼如下:
/**
* 丟擲 InterruptedException 的執行緒,中斷狀態被重置為預設狀態 false
*
* @author Jeff Lee @ bysocket.com
* @since 2018年02月23日19:03:02
*/
public class InterruptedException implements Runnable {
@Override // 可以省略
public void run() {
// 一直 sleep
try {
TimeUnit.SECONDS.sleep(10);
} catch (java.lang.InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
Thread interruptedThread = new Thread(new InterruptedException(), "InterruptedThread");
interruptedThread.start();
TimeUnit.SECONDS.sleep(2);
// 中斷被阻塞狀態(sleep、wait、join 等狀態)的執行緒,會丟擲異常 InterruptedException
// 在丟擲異常 InterruptedException 前,JVM 會先將中斷狀態重置為預設狀態 false
interruptedThread.interrupt();
System.out.println("InterruptedThread interrupted is " + interruptedThread.isInterrupted());
TimeUnit.SECONDS.sleep(2);
}
}
執行 main 函式,結果如下:
InterruptedThread interrupted is false
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
程式碼詳解:
- 中斷被阻塞狀態(sleep、wait、join 等狀態)的執行緒,會丟擲異常 InterruptedException
- 丟擲異常 InterruptedException 前,JVM 會先將中斷狀態重置為預設狀態 false
小結下執行緒中斷:
- 執行緒中斷,不是停止執行緒,只是一個執行緒的標誌位屬性
- 如果執行緒狀態為被阻塞狀態(sleep、wait、join 等狀態),執行緒狀態退出被阻塞狀態,丟擲異常 InterruptedException,並重置中斷狀態為預設狀態 false
- 如果執行緒狀態為執行狀態,執行緒狀態不變,繼續執行,中斷狀態置為 true
2 執行緒終止
比如在 IDEA 中強制關閉程式,立即停止程式,不給程式釋放資源等操作,肯定是不正確的。執行緒終止也存在類似的問題,所以需要考慮如何終止執行緒?
上面聊到了執行緒中斷,可以利用執行緒中斷標誌位屬性來安全終止執行緒。同理也可以使用 boolean 變數來控制是否需要終止執行緒。
新建 ,程式碼如下:
/**
* 安全終止執行緒
*
* @author Jeff Lee @ bysocket.com
* @since 2018年02月23日19:03:02
*/
public class ThreadSafeStop {
public static void main(String[] args) throws Exception {
Runner one = new Runner();
Thread countThread = new Thread(one, "CountThread");
countThread.start();
// 睡眠 1 秒,通知 CountThread 中斷,並終止執行緒
TimeUnit.SECONDS.sleep(1);
countThread.interrupt();
Runner two = new Runner();
countThread = new Thread(two,"CountThread");
countThread.start();
// 睡眠 1 秒,然後設定執行緒停止狀態,並終止執行緒
TimeUnit.SECONDS.sleep(1);
two.stopSafely();
}
private static class Runner implements Runnable {
private long i;
// 終止狀態
private volatile boolean on = true;
@Override
public void run() {
while (on && !Thread.currentThread().isInterrupted()) {
// 執行緒執行具體邏輯
i++;
}
System.out.println("Count i = " + i);
}
public void stopSafely() {
on = false;
}
}
}
從上面程式碼可以看出,通過 while (on && !Thread.currentThread().isInterrupted())
程式碼來實現執行緒是否跳出執行邏輯,並終止。但是疑問點就來了,為啥需要 on
和 isInterrupted()
兩項一起呢?用其中一個方式不就行了嗎?答案在下面
- 執行緒成員變數
on
通過 volatile 關鍵字修飾,達到執行緒之間可見,從而實現執行緒的終止。但當執行緒狀態為被阻塞狀態(sleep、wait、join 等狀態)時,對成員變數操作也阻塞,進而無法執行安全終止執行緒 - 為了處理上面的問題,引入了
isInterrupted();
只去解決阻塞狀態下的執行緒安全終止。 - 兩者結合是真的沒問題了嗎?不是的,如果是網路 io 阻塞,比如一個 websocket 一直再等待響應,那麼直接使用底層的 close 。
3 小結
很多好友介紹,如果用 Spring 棧開發到使用執行緒或者執行緒池,那麼儘量使用框架這塊提供的執行緒操作及框架提供的終止等
資料: 《Java 併發程式設計的藝術》