1. 程式人生 > >執行緒中斷 interrupt()

執行緒中斷 interrupt()

interrupt()是什麼?

	interrupt是Thread例項為我們提供的方法,通過這個方法可以向執行緒發出中斷訊號

為什麼要中斷?

	作為一個設計良好的執行緒類,在編寫每行程式碼時必須考慮特殊情況,其中一項就是中斷
	中斷的原因執行緒本身並不清除,但為了多執行緒協作程式設計,當收到中斷訊號時如何處理就
	成為我們執行緒類編寫中必修的一門功課

與stop()方法有何異同?

	早期JAVA版本中,對執行緒提供了stop()方法,它的作用是終止執行緒的執行。它是個刺客
	不告知理由,不選擇時間地點,上來就乾死你。
	這種粗暴的行為,通常帶來的後果就是資源無法釋放,死鎖等等

	因此,interrupt方法的出現,變得溫柔體貼。它只會通知執行緒中斷而並非終止,此外線
	程最終是什麼狀態仍舊由執行緒本身決定。換句話說,通知了執行緒中斷,但是執行緒不鳥你
	那相當於沒有發生任何事情,中斷訊號也將被丟棄

情景一:檢測中斷訊號並跳出程式碼段

        Thread singleThread = new Thread(new Runnable() {
            @Override
            public void run() {
                //正常執行中
                while (!Thread.currentThread().isInterrupted()){
                    System.out.println("It is run with Runnable");
                }
System.out.println("It is interrupted"); } }) ; singleThread.start(); Thread.sleep(5000); singleThread.interrupt();

啟動執行緒後,主執行緒休眠2秒之後,發出interrupt訊號 執行緒內判定interrupt訊號,為true時終止while迴圈後輸出最後的日誌

效果

It is run with Runnable
It is run with Runnable
It is run with Runnable
It is run with Runnable
It is run with Runnable
It is interrupted

Process finished with exit code 0

情景二:多處檢測中斷訊號

        Thread singleThread = new Thread(new Runnable() {
            @Override
            public void run() {
                //正常執行中
                while (!Thread.currentThread().isInterrupted()){
                    System.out.println("It is run with Runnable A");
                }
                System.out.println("It is interrupted A");

                //如果發生中斷,永遠都不會走到這裡
                while (!Thread.currentThread().isInterrupted()){
                    System.out.println("如果發生中斷,永遠都不會走到這裡");
                }
                System.out.println("直接走到了這裡");
            }
        }) ;
        singleThread.start();
        Thread.sleep(5000);
        singleThread.interrupt();

程式碼中有兩個迴圈,均對中斷訊號做了檢測。如果發生中斷事件,第一個迴圈檢測到並跳出了程式碼段。第二個迴圈繼續檢測訊號,於是迴圈都沒有進入直接跳出了程式碼段。

效果

It is run with Runnable A
It is run with Runnable A
It is run with Runnable A
It is run with Runnable A
It is interrupted A
直接走到了這裡

情景三:檢測訊號後重置訊號

        Thread singleThread = new Thread(new Runnable() {
            @Override
            public void run() {
                //正常執行中
                /**
                 * 這個是interrupted方法原始碼
                 public static boolean interrupted() {
                 return currentThread().isInterrupted(true);
                 }
                 */
                while (!Thread.interrupted()){
                    System.out.println("It is run with Runnable A");
                }
                System.out.println("It is interrupted A");

                while (!Thread.currentThread().isInterrupted()){
                    System.out.println("發生中斷但被重置了,會走到這裡");
                    //為了演示,我們直接跳出去
                    break;
                }
                System.out.println("最後走到了這裡");
            }
        }) ;
        singleThread.start();
        Thread.sleep(5000);
        singleThread.interrupt();

靜態方法interrupted() :它會檢測中斷訊號並將其重置 在第一個迴圈內,我們使用Thread.interrupted()進行對中斷訊號的判定,效果與Thread.currentThread().isInterrupted()一致。唯一不同的就是它判定之後,會將中斷訊號重置,也就是說下次再判定中斷訊號時,將是false。於是第二個迴圈也會正常進入。【為了演示我們主動break跳出】

效果

It is run with Runnable A
It is run with Runnable A
It is run with Runnable A
It is run with Runnable A
It is run with Runnable A
It is run with Runnable A
It is run with Runnable A
It is interrupted A
發生中斷但被重置了,會走到這裡
最後走到了這裡

情景四:檢測程式碼塊記憶體在InterruptedException

        Thread singleThread = new Thread(){
            @Override
            public void run() {
                //正常執行中
                while (!isInterrupted()){
                    try {
                        sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("即便發了中斷訊號,也檢測了中斷訊號,還是中斷不了");
                    //為了演示搞個隨機數跳出
                    if (Math.random()>0.8){
                        break;
                    }
                }
            }
        } ;
        singleThread.start();
        Thread.sleep(5000);
        singleThread.interrupt();

按照之前的寫法,發生中斷訊號後,程式碼段將跳出,迴圈將終止。但在本情景下,檢測中斷的程式碼塊記憶體在InterruptedException異常,這個異常如果不做處理,那麼中斷訊號將被丟棄,不會真的中斷執行緒

效果

即便發了中斷訊號,也檢測了中斷訊號,還是中斷不了
java.lang.InterruptedException: sleep interrupted
即便發了中斷訊號,也檢測了中斷訊號,還是中斷不了
	at java.lang.Thread.sleep(Native Method)
	at com.weiyinggo.study.CreateThread$1.run(CreateThread.java:25)
即便發了中斷訊號,也檢測了中斷訊號,還是中斷不了
即便發了中斷訊號,也檢測了中斷訊號,還是中斷不了

Process finished with exit code 0

為了解決這種問題,我們需要在異常catch段再次主動中斷執行緒才可以

        Thread singleThread = new Thread(){
            @Override
            public void run() {
                //正常執行中
                while (!isInterrupted()){
                    System.out.println("已經檢測到了中斷訊號");
                    try {
                        sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        interrupt();
                    }
                    System.out.println("已經再次進行了中斷");
                }
            }
        } ;
        singleThread.start();
        Thread.sleep(5000);
        singleThread.interrupt();
///////////////////////////////////////////
已經檢測到了中斷訊號
已經再次進行了中斷
已經檢測到了中斷訊號
已經再次進行了中斷
已經檢測到了中斷訊號
java.lang.InterruptedException: sleep interrupted
已經再次進行了中斷
	at java.lang.Thread.sleep(Native Method)
	at com.weiyinggo.study.CreateThread$1.run(CreateThread.java:26)

Process finished with exit code 0

總結

  1. 編寫執行緒類時要時刻考慮各種外來情況,比如本文中的interrupt
  2. 當執行緒中斷檢測程式碼塊中存在InterruptedException時要再次主動中斷,否則中斷訊號將被丟棄