如何優雅正確地通過interrupt方法中斷線程


為什么廢棄Thread的stop函數?

簡單來說就是stop方法中斷線程太過暴力隨意,且會是否線程持有的鎖,會導致線程安全問題。還有可能導致存在需要被釋放的資源得不到釋放,引發內存泄露。所以用stop停止線程是不推薦的。

  1. stop是通過立即拋出ThreadDeath異常,來達到停止線程的目的,此異常拋出有可能發生在任何一時間點,包括在catch、finally等語句塊中,但是此異常並不會引起程序退出。
  2. 由於有異常拋出,導致線程會釋放全部所持有的鎖,極可能引起線程安全問題。
  3. 有可能導致存在需要被釋放的資源得不到釋放,引發內存泄露。

使用Thread的interrupt()方法結束線程

調用Thread對象的interrupt函數並不是立即中斷線程,只是將線程中斷狀態標志設置為true,當線程運行中有調用其阻塞的函數(Thread.sleep,Object.wait,Thread.join等時,像Thread.sleep()方法調用之后,會不斷地輪詢檢測中斷狀態標志是否為true,如果為true,則停止阻塞並拋出InterruptedException異常,同時還會重置中斷狀態標志false;如果為false,我們在catch方法體里沒有重新interrupt()把在中斷狀態標志設置為true的話,就會導致循環體無需循環下去,可參考下面的代碼。

注意事項:

  • 使用isInterrupted來判斷線程是否處於中斷狀態,若是中斷狀態,則跳出正在執行的任務,使線程結束運行。
  • 調用sleep()方法觸發InterruptedException異常時,在catch代碼塊中需再次調用interrupt()方法,使線程再次處於中斷狀態,使while循環條件為false,使線程跳出循環,結束運行。若不調用,while循環為死循環,線程無法結束。

代碼示例

public class InterruptTest {

    /**
     * 結束未使用阻塞函數的線程
     * @throws InterruptedException
     */
    @Test
    public void test1() throws InterruptedException {
        Thread thread = new Thread(() -> {
            System.out.println("開始搬磚!");
            //只要代碼里沒有異常,那么我們可以通過判斷線程是否中斷來執行業務操作
            //當其他線程發出線程中斷信號后就會跳出正在執行的任務,往下執行使線程結束運行。
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("西西弗斯的陷阱:無腦循環,日復一日搬磚!");
            }
            System.out.println("神跡出現,結束搬磚!");
            System.out.println(Thread.currentThread().isInterrupted());//true
        });
        thread.start();
        TimeUnit.MILLISECONDS.sleep(10);
        System.out.println("啟動超級進化形態!");
        thread.interrupt();
    }
    /**
     * 開始搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 。。。。。
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 啟動超級進化形態!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 神跡出現,結束搬磚!
     */

    /**
     * 結束使用阻塞函數的線程
     * @throws InterruptedException
     */
    @Test
    public void test2() throws InterruptedException {
        Thread thread = new Thread(() -> {
            System.out.println("開始搬磚!");
            //只要代碼里沒有異常,那么我們可以通過判斷線程是否中斷來執行業務操作
            //當其他線程發出線程中斷信號后就會跳出正在執行的任務,往下執行使線程結束運行。
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("西西弗斯的陷阱:無腦循環,日復一日搬磚!");
                try {
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println(Thread.currentThread().isInterrupted());//false
                    Thread.currentThread().interrupt();
                    System.out.println(Thread.currentThread().isInterrupted());//true
                }
            }
            System.out.println("神跡出現,結束搬磚!");
        });
        thread.start();
        TimeUnit.MILLISECONDS.sleep(1000);
        System.out.println("啟動超級進化形態!");
        thread.interrupt();
        //主線程加上這個睡眠時間是為了便於查看當沒有在catch代碼塊中需調用interrupt函數,使線程再次處於中斷狀態,
        // 使while循環條件為false,使線程跳出循環,結束運行。若不調用,while循環為死循環,線程無法結束。
        TimeUnit.SECONDS.sleep(3000);
    }
    /**
     * catch中有調用 Thread.currentThread().interrupt();
     * 開始搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 啟動超級進化形態!
     * java.lang.InterruptedException: sleep interrupted
     * 	at java.lang.Thread.sleep(Native Method)
     * 	at java.lang.Thread.sleep(Thread.java:340)
     * 	at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
     * 	at cn.vv.schedule.module.rewardpunishment.service.InterruptTest.lambda$test2$1(InterruptTest.java:64)
     * 	at java.lang.Thread.run(Thread.java:748)
     * 神跡出現,結束搬磚!
     */

    /**
     * catch中沒有調用 Thread.currentThread().interrupt();線程會無需循環下去,直到主線程結束
     *開始搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 啟動超級進化形態!
     * java.lang.InterruptedException: sleep interrupted
     * 	at java.lang.Thread.sleep(Native Method)
     * 	at java.lang.Thread.sleep(Thread.java:340)
     * 	at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
     * 	at cn.vv.schedule.module.rewardpunishment.service.InterruptTest.lambda$test2$1(InterruptTest.java:64)
     * 	at java.lang.Thread.run(Thread.java:748)
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 西西弗斯的陷阱:無腦循環,日復一日搬磚!
     * 。。。。。。一直下去
     */

}

Thread的靜態函數interrupted與Thread的對象函數isInterrupted比較

靜態方法Thread.interrupted()會在檢測線程中斷狀態標志是否為true后,還會將中斷狀態標志重置為false

對象方法thread.isInterrupted()只是檢測線程中斷狀態標志

//靜態方法
public static boolean interrupted() {
    return currentThread().isInterrupted(true);
}
//對象方法
public boolean isInterrupted() {
    return isInterrupted(false);
}

/**
     * Tests if some Thread has been interrupted.  The interrupted state
     * is reset or not based on the value of ClearInterrupted that is
     * passed.
     */
//ClearInterrupted表示是否清除中斷標示狀態,清除后為false
private native boolean isInterrupted(boolean ClearInterrupted);

參考

Java線程中斷的理解和正確使用


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM