Java 多線程-如何正確的中斷線程

??在學(xué)習(xí)Java的過(guò)程中,多線程一直以來(lái)就是Java中的比較難的知識(shí)點(diǎn),但是多線程又是那么的重要。在實(shí)際的開發(fā)中,多線程也是常見的。本文將介紹在多線程中怎么正確的中斷線程,不會(huì)介紹多線程的基知識(shí)。

1. 前言

??在Java的線程中,只有線程的run方法運(yùn)行完畢,才算線程真正的結(jié)束。通常來(lái)說(shuō),一種情況是run方法正確無(wú)誤的運(yùn)行完畢,還有一種情況是在run方法里面拋出了異常并且沒(méi)有捕獲,導(dǎo)致線程異常終止。
??在早期的Java版本中,有一個(gè)stop方法,可以終止一個(gè)線程。但是這個(gè)方法已經(jīng)被棄用了。
??在現(xiàn)如今的Java版本中,沒(méi)有強(qiáng)制終止線程的方法。然而,我們可以通過(guò)interrupt方法來(lái)請(qǐng)求終止線程,這個(gè)操作需要程序員自己來(lái)實(shí)現(xiàn)。接下來(lái)會(huì)介紹interrupt方法的用法。
??當(dāng)我們對(duì)一個(gè)線程調(diào)用interrupt方法時(shí),線程有一個(gè)中斷狀態(tài)標(biāo)志位將會(huì)被重置。這個(gè)標(biāo)志位每一個(gè)線程都有。我們?cè)跇?gòu)建自己的多線程時(shí),應(yīng)該時(shí)不時(shí)的檢查這個(gè)標(biāo)志位,以判斷當(dāng)前線程是否需要終止。
??例如:

    @Override
    public void run() {
        while (true) {
            if (Thread.currentThread().isInterrupted()) {//中斷狀態(tài)標(biāo)志位被重置
                //退出死循環(huán),結(jié)束run方法
                break;
            } else { //沒(méi)有被重置
                //do your work
            }

        }
    }

??這里我舉了一個(gè)簡(jiǎn)單的例子。我們?cè)趓un方法中增加了一個(gè)判斷條件,只有當(dāng)前的中斷標(biāo)志位為false才進(jìn)行運(yùn)行的,否則就跳出死循環(huán),此時(shí)當(dāng)前的線程生命就就結(jié)束了。此時(shí),如果我們?cè)谄渌€程里面想要中斷這個(gè)線程,只需要調(diào)用interrupt方法就行了。

2. InterruptException異常產(chǎn)生的原因

??關(guān)于InterruptException異常,只要熟悉多線程的朋友可能都會(huì)認(rèn)識(shí),當(dāng)我們?cè)谡{(diào)用Thread.sleep方法時(shí),就會(huì)拋出這個(gè)異常,這里將介紹InterruptException異常的原因,并且將介紹這個(gè)異常造成的結(jié)果。
??前面我們已經(jīng)知道了,我們可以調(diào)用interrupt方法來(lái)重置當(dāng)前線程的中斷狀態(tài)標(biāo)志位。但是我們?cè)谡{(diào)用這個(gè)方法,有一個(gè)問(wèn)題,如果當(dāng)前的線程被阻塞了(線程的阻塞方式有很多, 比如,被Scanner阻塞,sleep,wait),是無(wú)法檢測(cè)到中斷狀態(tài),如果此時(shí)調(diào)用interrupt方法,在阻塞的位置會(huì)拋出一個(gè)異常,那就是InterruptException異常。這個(gè)也能解釋,為什么我們?cè)谡{(diào)用sleep方法時(shí),會(huì)拋出InterruptException方法。如果線程產(chǎn)生了InterruptException異常,會(huì)被異常中斷(也存在不能被中斷的線程阻塞,比如IO訪問(wèn))。

3. sleep方法的正確使用

??由于sleep方法會(huì)拋出InterruptException異常,所以在一個(gè)線程如何使用sleep方法就變得格外不一樣。為什么不一樣呢?可能各位朋友會(huì)這樣寫代碼:

        Thread thread = new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(500);
                    // 這里的線程工作就是不斷打印1
                    System.out.println(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(Thread.currentThread().isInterrupted()) {
                    break;
                }
            }
        });

??這個(gè)代碼通常來(lái)說(shuō)沒(méi)有人寫,這里只是為了舉一個(gè)例子。這個(gè)代碼感覺(jué)是沒(méi)有任何的問(wèn)題,如果此時(shí),我們這樣調(diào)用的話:

        thread.start();
        thread.interrupt();

??那么會(huì)出現(xiàn)一個(gè)奇怪的現(xiàn)象,就是我們調(diào)用interrupt方法,線程不會(huì)停止。這個(gè)又是什么原因呢?我們先按照常規(guī)的思維來(lái)思考一下:調(diào)用start方法線程開啟,先sleep,此時(shí)外部調(diào)用interrupt方法來(lái)重置中斷狀態(tài)標(biāo)志位,將flag(假設(shè)為中斷狀態(tài)標(biāo)志位)重置為true,但是此時(shí)會(huì)拋出一個(gè)InterruptException,這個(gè)的原因,我們?cè)谥耙呀?jīng)說(shuō)了,當(dāng)前的線程被sleep方法阻塞了,此時(shí)如果在調(diào)用當(dāng)前的interrupt方法,會(huì)拋出異常。拋出異常之后,進(jìn)入catch,這個(gè)沒(méi)的說(shuō),此時(shí)再進(jìn)入if判斷語(yǔ)句,如果當(dāng)前的flag為true,就會(huì)退出循環(huán)。
??實(shí)際上,在整個(gè)過(guò)程中,線程被sleep方法阻塞了,但是產(chǎn)生了InterruptException異常時(shí),此時(shí)線程不會(huì)繼續(xù)睡眠,而是馬上拋出異常,而且會(huì)清除flag的狀態(tài),也就是將flag變?yōu)閒alse。
??我們建議這樣做:

        Thread thread = new Thread(() -> {
            try {
                while (true) {
                    Thread.sleep(500);
                    // 這里的線程工作就是不斷打印1
                    System.out.println(1);
                    if (Thread.currentThread().isInterrupted()) {
                        break;
                    }
                }
            } catch (InterruptedException e) {
                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ù)。

相關(guān)閱讀更多精彩內(nèi)容

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