面試突擊:如何正確停止線程?
在 Java 中停止線程的實現(xiàn)方法有以下 3 種:
- 自定義中斷標(biāo)識符,停止線程。
- 使用線程中斷方法 interrupt 停止線程。
- 使用 stop 停止線程。
其中 stop 方法為 @Deprecated 修飾的過期方法,也就是不推薦使用的過期方法,因為 stop 方法會直接停止線程,這樣就沒有給線程足夠的時間來處理停止前的保存工作,就會造成數(shù)據(jù)不完整的問題,因此不建議使用。而自定義中斷標(biāo)識也有一些問題,所以綜合來看,interrupt 方法才是最理想的停止線程的方法,接下來我們一起來看它們的具體差異。
1.自定義中斷標(biāo)識符
自定義中斷標(biāo)識符就是在程序中定義一個變量來決定線程是否要中斷執(zhí)行,具體實現(xiàn)代碼如下:
class FlagThread extends Thread {
// 自定義中斷標(biāo)識符
public volatile boolean isInterrupt = false;
@Override
public void run() {
// 如果為 true -> 中斷執(zhí)行
while (!isInterrupt) {
// 業(yè)務(wù)邏輯處理
}
}
}
但自定義中斷標(biāo)識符的問題在于:線程中斷的不夠及時。因為線程在執(zhí)行過程中,無法調(diào)用 while(!isInterrupt) 來判斷線程是否為終止?fàn)顟B(tài),它只能在下一輪運行時判斷是否要終止當(dāng)前線程,所以它中斷線程不夠及時,比如以下代碼:
class InterruptFlag {
// 自定義的中斷標(biāo)識符
private static volatile boolean isInterrupt = false;
public static void main(String[] args) throws InterruptedException {
// 創(chuàng)建可中斷的線程實例
Thread thread = new Thread(() -> {
while (!isInterrupt) { // 如果 isInterrupt=true 則停止線程
System.out.println("thread 執(zhí)行步驟1:線程即將進入休眠狀態(tài)");
try {
// 休眠 1s
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread 執(zhí)行步驟2:線程執(zhí)行了任務(wù)");
}
});
thread.start(); // 啟動線程
// 休眠 100ms,等待 thread 線程運行起來
Thread.sleep(100);
System.out.println("主線程:試圖終止線程 thread");
// 修改中斷標(biāo)識符,中斷線程
isInterrupt = true;
}
}
以上代碼的執(zhí)行結(jié)果如下圖所示:
我們期望的是:線程執(zhí)行了步驟 1 之后,收到中斷線程的指令,然后就不要再執(zhí)行步驟 2 了,但從上述執(zhí)行結(jié)果可以看出,使用自定義中斷標(biāo)識符是沒辦法實現(xiàn)我們預(yù)期的結(jié)果的,這就是自定義中斷標(biāo)識符,響應(yīng)不夠及時的問題。
2.interrupt中斷線程
使用 interrupt 方法可以給執(zhí)行任務(wù)的線程,發(fā)送一個中斷線程的指令,它并不直接中斷線程,而是發(fā)送一個中斷線程的信號,把是否正在中斷線程的主動權(quán)交給代碼編寫者。相比于自定義中斷標(biāo)識符而然,它能更及時的接收到中斷指令,如下代碼所示:
public static void main(String[] args) throws InterruptedException {
// 創(chuàng)建可中斷的線程實例
Thread thread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("thread 執(zhí)行步驟1:線程即將進入休眠狀態(tài)");
try {
// 休眠 1s
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("thread 線程接收到中斷指令,執(zhí)行中斷操作");
// 中斷當(dāng)前線程的任務(wù)執(zhí)行
break;
}
System.out.println("thread 執(zhí)行步驟2:線程執(zhí)行了任務(wù)");
}
});
thread.start(); // 啟動線程
// 休眠 100ms,等待 thread 線程運行起來
Thread.sleep(100);
System.out.println("主線程:試圖終止線程 thread");
// 修改中斷標(biāo)識符,中斷線程
thread.interrupt();
}
以上代碼的執(zhí)行結(jié)果如下圖所示:
從上述結(jié)果可以看出,線程在接收到中斷指令之后,立即中斷了線程,相比于上一種自定義中斷標(biāo)識符的方法來說,它能更及時的響應(yīng)中斷線程指令。
3.stop停止線程
stop 方法雖然可以停止線程,但它已經(jīng)是不建議使用的廢棄方法了,這一點可以通過 Thread 類中的源碼發(fā)現(xiàn),stop 源碼如下:
從上面的圖片可以看出,stop 方法是被 @Deprecated 修飾的不建議使用的過期方法,并且在注釋的第一句話就說明了 stop 方法為非安全的方法。在最新版本 Java 中,此方法已經(jīng)被直接移除了,所以強烈不建議使用。
總結(jié)
本文介紹了停止線程的 3 種方法:
- 自定義中斷標(biāo)識符的停止方法,此方法的缺點是不能及時響應(yīng)中斷請求;
- 使用 interrupt 中斷線程方法,此方法是發(fā)送一個中斷信號給線程,它可以及時響應(yīng)中斷,也是最推薦使用的方法;
- 最后是 stop 方法,雖然它也可以停止線程,但此方法已經(jīng)是過時的不建議使用的方法,在 Java 最新版本中已經(jīng)被直接移除了,所以不建議使用。