1. 程式人生 > >棟哥帶你學Java執行緒中斷

棟哥帶你學Java執行緒中斷

執行緒中斷

(一)stop方法

在早期,執行緒中又一個stop方法,可以直接終止執行緒.但是現在已經不用了,而且api上也已經在方法下標明已過時.這時因為stop方法一旦呼叫,不管執行緒是什麼狀態的都直接會被終
止,假如正在執行執行緒,run方法中的程式碼執行到一半被中斷了,那麼如果是像銀行這些地
方,會造成不可預計的事情發生,所以是很危險的.

(二)interrupt方法

既然stop方法不能使用,那麼我就去api看看還有沒有別的方法,然後api中有這個方法
interrupt()中斷執行緒。那麼我們來測試一下.
我們先寫一個類實現Runnable介面,然後重寫run方法,在run方法裡寫個死迴圈列印任意
內容,再使用interrupt看看能不能中斷.
public class Test {
    public static void main(String[] args) {
        TestRunnable runnable = new TestRunnable();
        Thread t = new Thread(runnable);
        t.start();
        t.interrupt();
    }
}

class TestRunnable implements Runnable {

    @Override
    public void run() {
        while
(true) { System.out.println("..."); } } } 結果:一直在迴圈列印... 那麼這時候就該想想是不是自己的程式碼有問題.然後我又去api找方法,這時候很巧,看到了 isInterrupted方法,它是用來判斷執行緒是否已經中斷. 接著我們在main函式里加上這幾句. boolean interrupted = t.isInterrupted(); System.out.println(interrupted); if (interrupted == false) { t.interrupt(); } isInterrupted()是個布林型別的方法,執行緒如果是執行狀態的話,我們輸出一下結果, 發現返回的是false,返回false的時候說明該執行緒沒有中斷.那我們就加個if
判斷,如果 返回false就是執行緒還沒中斷,我們就呼叫interrupt()方法中斷執行緒,再次執行. 執行結果:發現還是死迴圈列印. 這時候不管你是什麼心態,既然api上有那就是這個方法是可以用的,只是我們的測試方式 沒到位,我們再去看文件.點進去interrupt()方法. public void interrupt() 中斷執行緒。 如果當前執行緒沒有中斷它自己(這在任何情況下都是允許的),則該執行緒的 checkAccess 方法就會被呼叫,這可能丟擲 SecurityException。 如果執行緒在呼叫 Object 類的 wait()、wait(long) 或 wait(long, int) 方法,或者該類的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法過程中受阻,則其中斷狀態將被清除,它還將收到一個 InterruptedException。 如果該執行緒在可中斷的通道上的 I/O 操作中受阻,則該通道將被關閉,該執行緒的中斷狀態將被設定並且該執行緒將收到一個 ClosedByInterruptException。 如果該執行緒在一個 Selector 中受阻,則該執行緒的中斷狀態將被設定,它將立即從選擇操作返回,並可能帶有一個非零值,就好像呼叫了選擇器的 wakeup 方法一樣。 如果以前的條件都沒有儲存,則該執行緒的中斷狀態將被設定。 中斷一個不處於活動狀態的執行緒不需要任何作用。 丟擲:SecurityException - 如果當前執行緒無法修改該執行緒 文件裡說了這麼一大堆,我們挑重點來看.執行緒在呼叫Object類的wait、join、sleep方 法後再去呼叫interrupt方法,那麼就會清除它們的中斷狀態,並且報一箇中斷異常---- InterruptedException. 這時候把程式碼改成我現在這個樣子⬇️ : public class Test { public static void main(String[] args) { TestRunnable runnable = new TestRunnable(); Thread t = new Thread(runnable); t.start(); boolean interrupted = t.isInterrupted(); System.out.println(interrupted); if
(interrupted == false) { t.interrupt(); boolean interrupted2 = t.isInterrupted(); System.out.println(interrupted2); } } } class TestRunnable implements Runnable { private int test = 10; @Override public void run() { while (test != 0) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("..."); test--; } } } 輸出結果: false true java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at coam.lanou3g.TestRunnable.run(Test.java:25) at java.lang.Thread.run(Thread.java:748) ... ... ... ... ... ... ... ... ... ... 解析:根據文件的說法,我在在使用了sleep方法後確實報了中斷異常,並且異常的右邊明 確指出是sleep interrupted,然後我們再看main函式裡,我線上程啟動後呼叫了一次 isInterrupted方法,然後再if裡面呼叫了interrupt中斷方法後,再次呼叫isInterrupted 方法,然後發現變成了ture.那麼我們就可以確定,呼叫interrupt方法清除狀態就是改變 isInterrupted方法的布林值.既然有了這個結論我們就再改一次程式碼. public static void main(String[] args) { TestRunnable runnable = new TestRunnable(); Thread t = new Thread(runnable); t.start(); System.out.println("執行緒啟動啦"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } t.interrupt(); System.out.println("執行緒關閉啦"); } class TestRunnable implements Runnable { @Override public void run() { while (!Thread.currentThread().isInterrupted()) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("..."); } } } 輸出結果: 執行緒啟動啦 ... 執行緒關閉啦 然後我們就會發現這次是真的被我們關閉啦. 解析:因為一開始執行緒啟動的時候,isInterrupted返回的是false,但是while只有為 true的時候才執行while裡面的程式碼,所以我們在main函式裡把中斷方法interrupt加 上就可以改變它的值,while(false)不執行執行緒就結束. 結論:實際上這個interrupt()方法是用來設定isInterrupted方法的返回值.如果執行緒 中有wait()、sleep()、join()等方法,這時呼叫interrupt方法就會丟擲一個異常 InterruptedException,並且清除中斷狀態,如果執行緒中沒有出現這幾個方法,那麼這 時呼叫interrupt方法,就直接設定中斷狀態(true/false的改變).

(三)使用標記法

先上程式碼再解析.
public class Test {
    public static void main(String[] args) {
        TestRunnable runnable = new TestRunnable();
        Thread t = new Thread(runnable);
        t.start();
        System.out.println("執行緒啟動啦");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        runnable.isOver = false;
        System.out.println("執行緒關閉啦");
    }
}

class TestRunnable implements Runnable {
    public boolean isOver = true;
    @Override
    public void run() {
        while (isOver) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println("...");
        }
    }
}
執行結果:
執行緒啟動啦
...
執行緒關閉啦
...
解析:標記法中斷方法有點類似,都是靠一個值的改變來終止執行緒.這段程式碼裡,測試類裡面
定義了成員變數isOver並設定初始值為true,是為了執行while裡面的程式碼.然後在run
方法裡面讓執行緒沉睡一秒鐘,主要是不睡眠打印出來太多東西了,然後在main函式裡,先開
啟執行緒,然後列印執行緒啟動(方便觀看效果),繼續讓執行緒沉睡一秒,然後把TestRunnable
類裡的成員變數isOver設為false,類裡用public修飾是方便直接呼叫就不設為私有了,
測試的時候不用那麼麻煩,然後我們再看效果.可以能有些人會不一樣,第二行的...沒有,
這是正常的,這要看是執行緒進入run方法後先結束睡眠還是main函式裡先修改類的成員有
關.

interrupt方法和標記法的使用場合
一般我們首選標記法來終止執行緒,interrupt方法能不使用就儘量不要使用,如果要停止執行緒,直接使用標記法.但是如果遇到了等待狀態時,可以使用interrupt方法,強行清楚該狀態interrupt方法.