欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

如何使用interrupt停止線程

本篇內(nèi)容介紹了“如何使用interrupt停止線程”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)建站!專注于網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、微信小程序開發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了新河免費(fèi)建站歡迎大家使用!

啟動(dòng)一個(gè)線程很簡(jiǎn)單,調(diào)用Thread類的start()方法,并在run()方法中定義需要執(zhí)行的任務(wù)就可以了,但是要正確的停止停止一個(gè)線程就需要注意了

一:如何正確的停止線程

通常情況下,我們不會(huì)手動(dòng)停止一個(gè)線程,而是允許線程運(yùn)行到結(jié)束,然后讓它自然停止。但是依然會(huì)有許多特殊的情況需要我們提前停止線程,比如:用戶突然關(guān)閉程序,或程序運(yùn)行出錯(cuò)重啟等。尤其是即將停止的線程在業(yè)務(wù)場(chǎng)景下仍然很有價(jià)值,我們就更需要正確的停止線程了。但是Java中并沒有直接提供能夠簡(jiǎn)單安全的停止線程的能力。

1.1 線程停止設(shè)計(jì)原則

Java 希望程序間能夠相互通知、相互協(xié)作地管理線程,因?yàn)槿绻涣私鈱?duì)方正在做的工作,貿(mào)然強(qiáng)制停止線程就可能會(huì)造成一些安全的問題,為了避免造成問題就需要給對(duì)方一定的時(shí)間來整理收尾工作。

比如:線程正在寫入一個(gè)文件,這時(shí)收到終止信號(hào),它就需要根據(jù)自身業(yè)務(wù)判斷,是選擇立即停止,還是將整個(gè)文件寫入成功后停止,而如果選擇立即停止就可能造成數(shù)據(jù)不完整,不管是中斷命令發(fā)起者,還是接收者都不希望數(shù)據(jù)出現(xiàn)問題。

因此對(duì)于Java而言最正確的停止線程的方式是使用 interrupt。因?yàn)閕nterrupt 僅僅起到通知被停止線程的作用。而對(duì)于被停止的線程而言,它擁有完全的自主權(quán),它既可以選擇立即停止,也可以選擇一段時(shí)間后停止,也可以選擇壓根不停止。

1.2 如何使用interrupt停止線程

while (!Thread.currentThread().isInterrupted() && has more work to do) {
    do more work
}

調(diào)用用某個(gè)線程的 interrupt() 方法之后,這個(gè)線程的中斷標(biāo)記位就會(huì)被設(shè)置成 true。每個(gè)線程都有這樣的標(biāo)記位,當(dāng)線程執(zhí)行時(shí),應(yīng)該定期檢查這個(gè)標(biāo)記位,如果標(biāo)記位被設(shè)置成 true,就說明有程序想終止該線程。在上面?zhèn)未a中可以看到,在 while 循環(huán)體判斷語句中,首先通過 Thread.currentThread().isInterrupt() 判斷線程是否被中斷,隨后檢查是否還有工作要做。

使用interrupt正確的停止線程:

public class StopThread implements Runnable{
    @Override
    public void run() {
        int count = 0;
        // 退出循環(huán)表示中斷標(biāo)志位被設(shè)置為了true(有人想停止線程)或任務(wù)完成
        while(!Thread.currentThread().isInterrupted() && count < 1000){
            // 中斷標(biāo)記為false(未改變)、且還有任務(wù)做則進(jìn)入循環(huán),每次都判斷一下
            System.out.println("count = " + count++);
        }
    }

    public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(new StopThread());
        thread.start();
        // 執(zhí)行當(dāng)前語句的線程進(jìn)行睡眠
        Thread.sleep(5);
        thread.interrupt();
    }
}

在 main 函數(shù)中會(huì)啟動(dòng)該子線程,然后主線程休眠 5 毫秒后立刻給該子線程發(fā)送中斷信號(hào),該子線程會(huì)檢測(cè)到中斷信號(hào),于是在還沒打印完1000個(gè)數(shù)的時(shí)候就會(huì)停下來,這種就屬于通過 interrupt 正確停止線程的情況。

1.2.1 線程在Sleep期間能否感受到interrupt中斷

Java 設(shè)計(jì)者在設(shè)計(jì)之初就考慮到了這一點(diǎn)。sleep、wait 等可以讓線程進(jìn)入阻塞的方法使線程休眠了,而處于休眠中的線程被中斷,那么線程是可以感受到中斷信號(hào)的,并且會(huì)拋出一個(gè) InterruptedException 異常,同時(shí)清除中斷信號(hào),將中斷標(biāo)記位設(shè)置成 false。這樣一來就不用擔(dān)心長(zhǎng)時(shí)間休眠中線程感受不到中斷了,因?yàn)榧幢憔€程還在休眠,仍然能夠響應(yīng)中斷通知,并拋出異常。

public class StopDuringSleep implements Runnable{

    @Override
    public void run() {
        int num = 0;
        while(!Thread.currentThread().isInterrupted() && num <= 1000){
            try {
                System.out.println("num = "+num++);
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

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

上面代碼的邏輯為:主線程休眠 5 毫秒后,通知子線程中斷,此時(shí)子線程仍在執(zhí)行 sleep 語句,處于休眠中。執(zhí)行代碼結(jié)果發(fā)現(xiàn)程序仍在繼續(xù)執(zhí)行,但拋出了InterruptedException異常信息。可以看出線程即使在在Sleep期間也能夠感受到interrupt中斷信息,程序仍在運(yùn)行沒有正確停止的原因是我們沒有正確處理這個(gè)異常。

在實(shí)際開發(fā)中,如果我們負(fù)責(zé)編寫的方法需要被別人調(diào)用,同時(shí)我們的方法內(nèi)調(diào)用了 sleep 或者 wait 等能響應(yīng)中斷的方法時(shí),僅僅 catch 住異常是不夠的。如上面的代碼所示,catch 語句塊里代碼是空的,它并沒有進(jìn)行任何處理。假設(shè)線程執(zhí)行到這個(gè)方法,并且正在 sleep,此時(shí)有線程發(fā)送 interrupt 通知試圖中斷線程,就會(huì)立即拋出異常,并清除中斷信號(hào)。拋出的異常被 catch 語句塊捕捉。但是,捕捉到異常的 catch 沒有進(jìn)行任何處理邏輯,相當(dāng)于把中斷信號(hào)給隱藏了,這樣做是非常不合理的,所以需要正確處理異常,兩種最佳處理異常的方法如下:

1)方法簽名拋異常,run() 強(qiáng)制 try/catch
void subTask2() throws InterruptedException {
    Thread.sleep(1000);
}

每一個(gè)方法的調(diào)用方有義務(wù)去處理異常。調(diào)用方要不使用 try/catch 并在 catch 中正確處理異常,要不將異常聲明到方法簽名中。如果每層邏輯都遵守規(guī)范,便可以將中斷信號(hào)層層傳遞到頂層,最終讓 run() 方法可以捕獲到異常。而對(duì)于 run() 方法而言,它本身沒有拋出 checkedException 的能力,只能通過 try/catch 來處理異常。層層傳遞異常的邏輯保障了異常不會(huì)被遺漏,而對(duì) run() 方法而言,就可以根據(jù)不同的業(yè)務(wù)邏輯來進(jìn)行相應(yīng)的處理。

2)再次中斷
private void reInterrupt() {
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        e.printStackTrace();
    }
}

在 catch 語句中再次中斷線程。如代碼所示,需要在 catch 語句塊中調(diào)用 Thread.currentThread().interrupt() 函數(shù)。因?yàn)槿绻€程在休眠期間被中斷,那么會(huì)自動(dòng)清除中斷信號(hào)。如果這時(shí)手動(dòng)添加中斷信號(hào),中斷信號(hào)依然可以被捕捉到。這樣后續(xù)執(zhí)行的方法依然可以檢測(cè)到這里發(fā)生過中斷,可以做出相應(yīng)的處理,整個(gè)線程可以正常退出。

二:錯(cuò)誤的停止線程的方法

2.1 常見的錯(cuò)誤停止線程的方法

幾種停止線程的錯(cuò)誤方法。比如 stop(),suspend() 和 resume(),這些方法已經(jīng)被 Java 直接標(biāo)記為 @Deprecated。我們不應(yīng)該再使用它們了。

  • stop():會(huì)直接把線程停止,這樣就沒有給線程足夠的時(shí)間來處理想要在停止前保存數(shù)據(jù)的邏輯,任務(wù)戛然而止,會(huì)導(dǎo)致出現(xiàn)數(shù)據(jù)完整性等問題。

  • suspend() 和 resume() :它們的問題在于如果線程調(diào)用 suspend(),它并不會(huì)釋放鎖,就開始進(jìn)入休眠,但此時(shí)有可能仍持有鎖,這樣就容易導(dǎo)致死鎖問題,因?yàn)檫@把鎖在線程被 resume() 之前,是不會(huì)被釋放的。

例如:設(shè)線程 A 調(diào)用了 suspend() 方法讓線程 B 掛起,線程 B 進(jìn)入休眠,而線程 B 又剛好持有一把鎖,此時(shí)假設(shè)線程 A 想訪問線程 B 持有的鎖,但由于線程 B 并沒有釋放鎖就進(jìn)入休眠了,所以對(duì)于線程 A 而言,此時(shí)拿不到鎖,也會(huì)陷入阻塞,那么線程 A 和線程 B 就都無法繼續(xù)向下執(zhí)行。

2.2 為什么用 volatile 標(biāo)記位的停止線程方法是錯(cuò)誤的

volatile 修飾標(biāo)記位適用的場(chǎng)景

public class VolatileCanStop implements Runnable{

    private volatile boolean canceled = false;

    @Override
    public void run() {
        int num = 0;
        try {
            while (!canceled && num <= 1000000) {
                if (num % 10 == 0) {
                    System.out.println(num + "是10的倍數(shù)。");
                }
                num++;
                Thread.sleep(1);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws InterruptedException {

        VolatileCanStop volatileCanStop = new VolatileCanStop();
        Thread thread = new Thread(volatileCanStop);
        thread.start();
        Thread.sleep(300);
        volatileCanStop.canceled = true;
    }
}

run() 方法中進(jìn)行 while 循環(huán),在循環(huán)體中又進(jìn)行了兩層判斷,首先判斷 canceled 變量的值,canceled 變量是一個(gè)被 volatile 修飾的初始值為 false 的布爾值,當(dāng)該值變?yōu)?true 時(shí),while 跳出循環(huán),while 的第二個(gè)判斷條件是 num 值小于1000000(一百萬),在while 循環(huán)體里,只要是 10 的倍數(shù)就打印出來,然后 num++。

接下來,首先啟動(dòng)線程,然后經(jīng)過 3 秒鐘的時(shí)間,把用 volatile 修飾的布爾值的標(biāo)記位設(shè)置成 true,這樣,正在運(yùn)行的線程就會(huì)在下一次 while 循環(huán)中判斷出 canceled 的值已經(jīng)變成 true 了,這樣就不再滿足 while 的判斷條件,跳出整個(gè) while 循環(huán),線程就停止了,這種情況是演示 volatile 修飾的標(biāo)記位可以正常工作的情況,但是如果我們說某個(gè)方法是正確的,那么它應(yīng)該不僅僅是在一種情況下適用,而在其他情況下也應(yīng)該是適用的

volatile 修飾標(biāo)記位不適用的場(chǎng)景

public class VolatileCannotStop {

   /**
    * 生產(chǎn)者
    */
   static class Producer implements Runnable{

       public volatile boolean canceled = false;
       BlockingQueue storage;

       public Producer(BlockingQueue storage) {
           this.storage = storage;
       }

       @Override
       public void run() {
           try{
               int num = 0;
               // 停止線程信號(hào)為true,且任務(wù)執(zhí)行完畢
               while(num <= 100000 && !canceled){
                   if (num % 50 == 0) {
                       // 生產(chǎn)者隊(duì)列滿了之后,會(huì)使當(dāng)前線程阻塞,阻塞后(被叫醒前)不能進(jìn)入下一次循環(huán),此時(shí)是感知不到canceled的狀態(tài)的
                       storage.put(num);
                       System.out.println(num + "是50的倍數(shù),被放到倉庫中了。");
                   }
                   num++;
               }
           }catch (InterruptedException e){
               e.printStackTrace();
           }finally {
               System.out.println("生產(chǎn)者結(jié)束運(yùn)行");
           }
       }
   }

   /**
    * 消費(fèi)者
    */
   static class Consumer{

       BlockingQueue storage;

       public Consumer(BlockingQueue storage) {
           this.storage = storage;
       }
       public boolean needMoreNums() {
           if (Math.random() > 0.97) {
               return false;
           }
           return true;
       }
   }

   public static void main(String[] args) throws InterruptedException {

       ArrayBlockingQueue storage = new ArrayBlockingQueue(8);

       Producer producer = new Producer(storage);
       Thread producerThread = new Thread(producer);
       producerThread.start();
       Thread.sleep(500);

       Consumer consumer = new Consumer(storage);
       while (consumer.needMoreNums()) {
           System.out.println(consumer.storage.take() + "被消費(fèi)了");
           Thread.sleep(100);
       }
       System.out.println("消費(fèi)者不需要更多數(shù)據(jù)了。");

       //一旦消費(fèi)不需要更多數(shù)據(jù)了,我們應(yīng)該讓生產(chǎn)者也停下來,但是實(shí)際情況卻停不下來
       producer.canceled = true;
       System.out.println(producer.canceled);
   }
}

main 函數(shù)中,首先創(chuàng)建了生產(chǎn)者/消費(fèi)者共用的倉庫 BlockingQueue storage,倉庫容量是 8,并且建立生產(chǎn)者并將生產(chǎn)者放入線程后啟動(dòng)線程,啟動(dòng)后進(jìn)行 500 毫秒的休眠,休眠時(shí)間保障生產(chǎn)者有足夠的時(shí)間把倉庫塞滿,而倉庫達(dá)到容量后就不會(huì)再繼續(xù)往里塞,這時(shí)生產(chǎn)者會(huì)阻塞,500 毫秒后消費(fèi)者也被創(chuàng)建出來,并判斷是否需要使用更多的數(shù)字,然后每次消費(fèi)后休眠 100 毫秒。

當(dāng)消費(fèi)者不再需要數(shù)據(jù),就會(huì)將 canceled 的標(biāo)記位設(shè)置為 true,理論上此時(shí)生產(chǎn)者會(huì)跳出 while 循環(huán),打印輸出canceled的值為true,并打印輸出“生產(chǎn)者運(yùn)行結(jié)束”。

然而結(jié)果卻不是我們想象的那樣,盡管已經(jīng)把 canceled 設(shè)置成 true,但生產(chǎn)者仍然沒有停止,這是因?yàn)樵谶@種情況下,生產(chǎn)者在執(zhí)行 storage.put(num) 時(shí)發(fā)生阻塞,在它被叫醒之前是沒有辦法進(jìn)入下一次循環(huán)判斷 canceled 的值的,所以在這種情況下用 volatile 是沒有辦法讓生產(chǎn)者停下來的,相反如果用 interrupt 語句來中斷,即使生產(chǎn)者處于阻塞狀態(tài),仍然能夠感受到中斷信號(hào),并做響應(yīng)處理

“如何使用interrupt停止線程”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

分享文章:如何使用interrupt停止線程
文章來源:http://chinadenli.net/article34/gogdse.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供自適應(yīng)網(wǎng)站靜態(tài)網(wǎng)站服務(wù)器托管軟件開發(fā)關(guān)鍵詞優(yōu)化定制網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站網(wǎng)頁設(shè)計(jì)