Java中如何正確停止線程?兩種停止線程最佳方法

如何正確停止線程

使用 interrupt 來通知思喊,而不是強(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("線程已停止S咔恰!");
                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秒的過程中,收到interrupt信號被打斷纲辽,
線程正在sleep過程中響應(yīng)中斷的方式就是拋出 InterruptedException 異常
運(yùn)行結(jié)果:

0是100的倍數(shù)
100是100的倍數(shù)
200是100的倍數(shù)
300是100的倍數(shù)
線程已停止Q瘴洹!
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)每次迭代都會讓線程阻塞一段時(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ù)
……
……

會發(fā)現(xiàn)雖然拋出了異常吊档,但是程序并沒有停止篙议,還在繼續(xù)輸出,
即使在while條件判斷處添加 !Thread.currentThread().isInterrupted() 條件籍铁,依然不能停止程序涡上!
原因:
java語言在設(shè)計(jì)sleep函數(shù)時(shí),有這樣一個(gè)理念:就是當(dāng)它一旦響應(yīng)中斷拒名,便會把interrupt標(biāo)記位清除吩愧。
也就是說雖然線程在sleep過程中收到了interrupt中斷通知,并且也捕獲到了異常打印了異常信息增显,但是由于sleep設(shè)計(jì)理念雁佳,導(dǎo)致Thread.currentThread().isInterrupted()標(biāo)記位會被清除,所以才會導(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();來恢復(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)容合作請聯(lián)系作者
  • 序言:七十年代末如暖,一起剝皮案震驚了整個(gè)濱河市笆檀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌盒至,老刑警劉巖酗洒,帶你破解...
    沈念sama閱讀 221,430評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異枷遂,居然都是意外死亡樱衷,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評論 3 398
  • 文/潘曉璐 我一進(jìn)店門登淘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來箫老,“玉大人,你說我怎么就攤上這事黔州∷w蓿” “怎么了?”我有些...
    開封第一講書人閱讀 167,834評論 0 360
  • 文/不壞的土叔 我叫張陵流妻,是天一觀的道長牲蜀。 經(jīng)常有香客問我,道長绅这,這世上最難降的妖魔是什么涣达? 我笑而不...
    開封第一講書人閱讀 59,543評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮证薇,結(jié)果婚禮上度苔,老公的妹妹穿的比我還像新娘。我一直安慰自己浑度,他們只是感情好寇窑,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,547評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著箩张,像睡著了一般甩骏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上先慷,一...
    開封第一講書人閱讀 52,196評論 1 308
  • 那天饮笛,我揣著相機(jī)與錄音,去河邊找鬼论熙。 笑死福青,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播无午,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼二蓝,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了指厌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,671評論 0 276
  • 序言:老撾萬榮一對情侶失蹤踊跟,失蹤者是張志新(化名)和其女友劉穎踩验,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體商玫,經(jīng)...
    沈念sama閱讀 46,221評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡箕憾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,303評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了拳昌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片袭异。...
    茶點(diǎn)故事閱讀 40,444評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖炬藤,靈堂內(nèi)的尸體忽然破棺而出御铃,到底是詐尸還是另有隱情,我是刑警寧澤沈矿,帶...
    沈念sama閱讀 36,134評論 5 350
  • 正文 年R本政府宣布上真,位于F島的核電站,受9級特大地震影響羹膳,放射性物質(zhì)發(fā)生泄漏睡互。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,810評論 3 333
  • 文/蒙蒙 一陵像、第九天 我趴在偏房一處隱蔽的房頂上張望就珠。 院中可真熱鬧,春花似錦醒颖、人聲如沸妻怎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蹂季。三九已至,卻和暖如春疏日,著一層夾襖步出監(jiān)牢的瞬間偿洁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評論 1 272
  • 我被黑心中介騙來泰國打工沟优, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留涕滋,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,837評論 3 376
  • 正文 我出身青樓挠阁,卻偏偏與公主長得像宾肺,于是被迫代替她去往敵國和親溯饵。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,455評論 2 359