阿里P8聊並發編程:線程中斷和終止
一、線程中斷
1.什麼是線程中斷?
線程中斷是線程的標誌位屬性。而不是真正終止線程,和線程的狀態無關。線程中斷過程表示一個運行中的線程,通過其他線程調用了該線程的 interrupt() 方法,使得該線程中斷標誌位屬性改變。
深入思考下,線程中斷不是去中斷了線程,恰恰是用來通知該線程應該被中斷了。具體是一個標誌位屬性,到底該線程生命週期是去終止,還是繼續運行,由線程根據標誌位屬性自行處理。
2.線程中斷操作
調用線程的 interrupt() 方法,根據線程不同的狀態會有不同的結果。
下面新建 InterruptedThread 對象,代碼如下:
/**
* 一直運行的線程,中斷狀態為 true
*
* @author Jeff Lee @ bysocket.com
* @since 2019年04月17日15: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 2019年04月17日15: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
代碼:https://github.com/JeffLi1993/java-concurrency-core-learning
二.線程終止
比如在 IDEA 中強制關閉程序,立即停止程序,不給程序釋放資源等操作,肯定是不正確的。線程終止也存在類似的問題,所以需要考慮如何終止線程?
上面聊到了線程中斷,可以利用線程中斷標誌位屬性來安全終止線程。同理也可以使用 boolean 變數來控制是否需要終止線程。
新建 ,代碼如下:
/**
* 安全終止線程
*
* @author Jeff Lee @ bysocket.com
* @since 2019年04月17日15: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 。三、小結
很多好友介紹,如果用 Spring 棧開發到使用線程或者線程池,那麼盡量使用框架這塊提供的線程操作及框架提供的終止等
本文的重點是你有沒有收穫與成長,其餘的都不重要,希望讀者們能謹記這一點。同時我經過多年的收藏目前也算收集到了一套完整的學習資料,包括但不限於:分散式架構、高可擴展、高性能、高並發、Jvm性能調優、Spring,MyBatis,Nginx源碼分析,Redis,ActiveMQ、、Mycat、Netty、Kafka、Mysql、Zookeeper、Tomcat、Docker、Dubbo、Nginx等多個知識點高級進階乾貨,希望對想成為架構師的朋友有一定的參考和幫助。
需要更詳細思維導圖和以下資料的:後臺私信「資料」即可免費領取
喜歡這篇文章的可以點個贊,也可以關注我的專欄 java經驗分享 ,專欄頂部有免費獲取資料方式