我們該如何正確的中斷一個正在執(zhí)行的線程??
作者個人研發(fā)的在高并發(fā)場景下,提供的簡單、穩(wěn)定、可擴(kuò)展的延遲消息隊列框架,具有精準(zhǔn)的定時任務(wù)和延遲隊列處理功能。自開源半年多以來,已成功為十幾家中小型企業(yè)提供了精準(zhǔn)定時調(diào)度方案,經(jīng)受住了生產(chǎn)環(huán)境的考驗。為使更多童鞋受益,現(xiàn)給出開源框架地址:https://github.com/sunshinelyz/mykit-delay
寫在前面
當(dāng)我們在調(diào)用Java對象的wait()方法或者線程的sleep()方法時,需要捕獲并處理InterruptedException異常。如果我們對InterruptedException異常處理不當(dāng),則會發(fā)生我們意想不到的后果!今天,我們就以一個案例的形式,來為大家詳細(xì)介紹下為何中斷執(zhí)行的線程不起作用。
程序案例
例如,下面的程序代碼,InterruptedTask類實現(xiàn)了Runnable接口,在run()方法中,獲取當(dāng)前線程的句柄,并在while(true)循環(huán)中,通過isInterrupted()方法來檢測當(dāng)前線程是否被中斷,如果當(dāng)前線程被中斷就退出while(true)循環(huán),同時,在while(true)循環(huán)中,還有一行Thread.sleep(100)代碼,并捕獲了InterruptedException異常。整個代碼如下所示。
- package io.binghe.concurrent.lab08;
- /**
- * @author binghe
- * @version 1.0.0
- * @description 線程測試中斷
- */
- public class InterruptedTask implements Runnable{
- @Override
- public void run() {
- Thread currentThread = Thread.currentThread();
- while (true){
- if(currentThread.isInterrupted()){
- break;
- }
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
上述代碼的本意是通過isInterrupted()方法檢查線程是否被中斷了,如果中斷了就退出while循環(huán)。其他線程通過調(diào)用執(zhí)行線程的interrupt()方法來中斷執(zhí)行線程,此時會設(shè)置執(zhí)行線程的中斷標(biāo)志位,從而使currentThread.isInterrupted()返回true,這樣就能夠退出while循環(huán)。
這看上去沒啥問題啊!但真的是這樣嗎?我們創(chuàng)建一個InterruptedTest類用于測試,代碼如下所示。
- package io.binghe.concurrent.lab08;
- /**
- * @author binghe
- * @version 1.0.0
- * @description 測試線程中斷
- */
- public class InterruptedTest {
- public static void main(String[] args){
- InterruptedTask interruptedTask = new InterruptedTask();
- Thread interruptedThread = new Thread(interruptedTask);
- interruptedThread.start();
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- interruptedThread.interrupt();
- }
- }
我們運行main方法,如下所示。
這竟然跟我們想象的不一樣!不一樣!不一樣!這是為什么呢?
問題分析
上述代碼明明調(diào)用了線程的interrupt()方法來中斷線程,但是卻并沒有起到啥作用。原因是線程的run()方法在執(zhí)行的時候,大部分時間都是阻塞在sleep(100)上,當(dāng)其他線程通過調(diào)用執(zhí)行線程的interrupt()方法來中斷執(zhí)行線程時,大概率的會觸發(fā)InterruptedException異常,在觸發(fā)InterruptedException異常的同時,JVM會同時把線程的中斷標(biāo)志位清除,所以,這個時候在run()方法中判斷的currentThread.isInterrupted()會返回false,也就不會退出當(dāng)前while循環(huán)了。
既然問題分析清楚了,那如何中斷線程并退出程序呢?
問題解決
正確的處理方式應(yīng)該是在InterruptedTask類中的run()方法中的while(true)循環(huán)中捕獲異常之后重新設(shè)置中斷標(biāo)志位,所以,正確的InterruptedTask類的代碼如下所示。
- package io.binghe.concurrent.lab08;
- /**
- * @author binghe
- * @version 1.0.0
- * @description 中斷線程測試
- */
- public class InterruptedTask implements Runnable{
- @Override
- public void run() {
- Thread currentThread = Thread.currentThread();
- while (true){
- if(currentThread.isInterrupted()){
- break;
- }
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- e.printStackTrace();
- currentThread.interrupt();
- }
- }
- }
- }
可以看到,我們在捕獲InterruptedException異常的catch代碼塊中新增了一行代碼。
- currentThread.interrupt();
這就使得我們捕獲到InterruptedException異常后,能夠重新設(shè)置線程的中斷標(biāo)志位,從而中斷當(dāng)前執(zhí)行的線程。
我們再次運行InterruptedTest類的main方法,如下所示。
總結(jié)
處理InterruptedException異常時要小心,如果在調(diào)用執(zhí)行線程的interrupt()方法中斷執(zhí)行線程時,拋出了InterruptedException異常,則在觸發(fā)InterruptedException異常的同時,JVM會同時把執(zhí)行線程的中斷標(biāo)志位清除,此時調(diào)用執(zhí)行線程的isInterrupted()方法時,會返回false。此時,正確的處理方式是在執(zhí)行線程的run()方法中捕獲到InterruptedException異常,并重新設(shè)置中斷標(biāo)志位(也就是在捕獲InterruptedException異常的catch代碼塊中,重新調(diào)用當(dāng)前線程的interrupt()方法)。
本文轉(zhuǎn)載自微信公眾號「冰河技術(shù)」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系冰河技術(shù)公眾號。