Java中如何正確停止線程??jī)煞N停止線程最佳方法

如何正確停止線程

使用 interrupt 來(lái)通知,而不是強(qiáng)制

1:普通情況停止線程

public class RightWayStopThreadWithoutSleep implements Runnable {

    @Override
    public void run() {
        int num = 0;
        while (!Thread.currentThread().isInterrupted() && num <= Integer.MAX_VALUE / 2) {
            if (num % 10000 == 0) {
                System.out.println(num + "是1W的倍數(shù)");
            }
            num++;
        }
        System.out.println("任務(wù)運(yùn)行結(jié)束!");
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new RightWayStopThreadWithoutSleep());
        thread.start();
        Thread.sleep(1000);
        // 通知停止線程
        thread.interrupt();
    }
}

通知停止線程
thread.interrupt();
并且線程需要配合
Thread.currentThread().isInterrupted()
運(yùn)行結(jié)果:

……
……
221730000是1W的倍數(shù)
221740000是1W的倍數(shù)
221750000是1W的倍數(shù)
221760000是1W的倍數(shù)
221770000是1W的倍數(shù)
221780000是1W的倍數(shù)
221790000是1W的倍數(shù)
221800000是1W的倍數(shù)
任務(wù)運(yùn)行結(jié)束!

Process finished with exit code 0

2:線程可能被阻塞情況下停止線程

public class RightWayStopThreadWithSleep {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = () -> {
            int num = 0;
            while (num <= 300 && !Thread.currentThread().isInterrupted()) {
                if (num % 100 == 0) {
                    System.out.println(num + "是100的倍數(shù)");
                }
                num++;
            }
            try {
                // 等個(gè)1秒,模擬阻塞
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println("線程已停止!!");
                e.printStackTrace();
            }
        };

        Thread thread = new Thread(runnable);
        thread.start();
        // 等待時(shí)間要小于上面設(shè)置的1秒,不然線程都運(yùn)行結(jié)束了,才執(zhí)行到下面的thread.interrupt();代碼
        Thread.sleep(500);
        // 通知停止線程
        thread.interrupt();
    }
}

線程在sleep 1秒的過(guò)程中,收到interrupt信號(hào)被打斷,
線程正在sleep過(guò)程中響應(yīng)中斷的方式就是拋出 InterruptedException 異常
運(yùn)行結(jié)果:

0是100的倍數(shù)
100是100的倍數(shù)
200是100的倍數(shù)
300是100的倍數(shù)
線程已停止!!
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at stopthreads.RightWayStopThreadWithSleep.lambda$main$0(RightWayStopThreadWithSleep.java:19)
    at java.lang.Thread.run(Thread.java:748)

Process finished with exit code 0

3:線程在每次迭代后都阻塞的情況下停止線程

public class RightWayStopThreadWithSleepEveryLoop {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = () -> {
            int num = 0;
            try {
                while (num <= 10000) {
                    if (num % 100 == 0) {
                        System.out.println(num + "是100的倍數(shù)");
                    }
                    num++;
                    // 每次循環(huán)都要等待10毫秒,模擬阻塞
                    Thread.sleep(10);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        Thread thread = new Thread(runnable);
        thread.start();
        // 5秒后通知停止線程
        Thread.sleep(5000);
        thread.interrupt();
    }
}

當(dāng)每次迭代都會(huì)讓線程阻塞一段時(shí)間的時(shí)候,在while/for循環(huán)條件判斷時(shí),是不需要判斷線程是否中斷的Thread.currentThread().isInterrupted()

如果將上述代碼的 try/catch 放在 while 循環(huán)內(nèi)

public class RightWayStopThreadWithSleepEveryLoop {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = () -> {
            int num = 0;
            while (num <= 10000) {
                if (num % 100 == 0) {
                    System.out.println(num + "是100的倍數(shù)");
                }
                num++;
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        Thread thread = new Thread(runnable);
        thread.start();
        Thread.sleep(5000);
        thread.interrupt();
    }
}

運(yùn)行結(jié)果:

0是100的倍數(shù)
100是100的倍數(shù)
200是100的倍數(shù)
300是100的倍數(shù)
400是100的倍數(shù)
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at stopthreads.RightWayStopThreadWithSleepEveryLoop.lambda$main$0(RightWayStopThreadWithSleepEveryLoop.java:18)
    at java.lang.Thread.run(Thread.java:748)
500是100的倍數(shù)
600是100的倍數(shù)
700是100的倍數(shù)
……
……

會(huì)發(fā)現(xiàn)雖然拋出了異常,但是程序并沒(méi)有停止,還在繼續(xù)輸出,
即使在while條件判斷處添加 !Thread.currentThread().isInterrupted() 條件,依然不能停止程序!
原因:
java語(yǔ)言在設(shè)計(jì)sleep函數(shù)時(shí),有這樣一個(gè)理念:就是當(dāng)它一旦響應(yīng)中斷,便會(huì)把interrupt標(biāo)記位清除。
也就是說(shuō)雖然線程在sleep過(guò)程中收到了interrupt中斷通知,并且也捕獲到了異常打印了異常信息,但是由于sleep設(shè)計(jì)理念,導(dǎo)致Thread.currentThread().isInterrupted()標(biāo)記位會(huì)被清除,所以才會(huì)導(dǎo)致程序不能退出。

兩種停止線程最佳方法

1. 捕獲了InterruptedException之后的優(yōu)先選擇:在方法簽名中拋出異常

public class RightWayStopThreadInProd implements Runnable {

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new RightWayStopThreadInProd());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }

    @Override
    public void run() {
        while (true) {
            System.out.println("go...");
            try {
                throwInMethod();
            } catch (InterruptedException e) {
                // 捕獲異常,進(jìn)行保存日志、停止程序等操作
                System.out.println("stop");
                e.printStackTrace();
            }
        }
    }

    /**
     * 如果方法內(nèi)要拋出異常,最好是將異常拋出去,由頂層的調(diào)用方去處理,而不是try/catch
     * 這樣調(diào)用方才能捕獲異常并作出其它操作
     * @throws InterruptedException
     */
    private void throwInMethod() throws InterruptedException {
        Thread.sleep(2000);
    }
}

如果方法內(nèi)要拋出異常,最好是將異常拋出去,由頂層的調(diào)用方去處理,而不是try/catch
這樣調(diào)用方才能捕獲異常并做出其它操作。

2. 在catch中調(diào)用Thread.currentThread().interrupt();來(lái)恢復(fù)設(shè)置中斷狀態(tài)

public class RightWayStopThreadInProd2 implements Runnable {

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new RightWayStopThreadInProd2());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }

    @Override
    public void run() {
        while (true) {
            if (Thread.currentThread().isInterrupted()) {
                System.out.println("程序運(yùn)行結(jié)束");
                break;
            }
            reInterrupt();
        }
    }

    private void reInterrupt() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            e.printStackTrace();
        }
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容