最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

一文揭曉如何在Java中終止一個(gè)線程

 更新時(shí)間:2023年03月09日 14:10:14   作者:flydean程序那些事  
工作中我們經(jīng)常會(huì)用到線程,一般情況下我們讓線程執(zhí)行就完事了,那么你們有沒(méi)有想過(guò)如何去終止一個(gè)正在運(yùn)行的線程呢?本文就來(lái)帶大家一起看看

簡(jiǎn)介

工作中我們經(jīng)常會(huì)用到線程,一般情況下我們讓線程執(zhí)行就完事了,那么你們有沒(méi)有想過(guò)如何去終止一個(gè)正在運(yùn)行的線程呢?

今天帶大家一起來(lái)看看。

Thread.stop被禁用之謎

問(wèn)道怎么終止一個(gè)線程,可能大多數(shù)人都知道可以調(diào)用Thread.stop方法。

但是這個(gè)方法從jdk1.2之后就不推薦使用了,為什么不推薦使用呢?

我們先來(lái)看下這個(gè)方法的定義:

  @Deprecated(since="1.2")
    public final void stop() {
        @SuppressWarnings("removal")
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            checkAccess();
            if (this != Thread.currentThread()) {
                security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
            }
        }
        // A zero status value corresponds to "NEW", it can't change to
        // not-NEW because we hold the lock.
        if (threadStatus != 0) {
            resume(); // Wake up thread if it was suspended; no-op otherwise
        }

        // The VM can handle all thread states
        stop0(new ThreadDeath());
    }

從代碼我們可以看出,stop這個(gè)方法首先檢測(cè)有沒(méi)有線程訪問(wèn)的權(quán)限。如果有權(quán)限的話,來(lái)判斷當(dāng)前的線程是否是剛剛創(chuàng)建的線程,如果不是剛剛創(chuàng)建的,那么就調(diào)用resume方法來(lái)解除線程的暫停狀態(tài)。

最后調(diào)用stop0方法來(lái)結(jié)束線程。

其中resume和stop0是兩個(gè)native的方法,具體的實(shí)現(xiàn)這里就不講了。

看起來(lái)stop方法很合理,沒(méi)有什么問(wèn)題。那么為什么說(shuō)這個(gè)方法是不安全的呢?

接下來(lái)我們來(lái)看一個(gè)例子。

我們創(chuàng)建一個(gè)NumberCounter的類,這個(gè)類有一個(gè)increaseNumber的安全方法,用來(lái)對(duì)number加一:

public class NumberCounter {
    //要保存的數(shù)字
    private volatile int number=0;
    //數(shù)字計(jì)數(shù)器的邏輯是否完整
    private volatile boolean flag = false;

    public synchronized int increaseNumber() throws InterruptedException {
        if(flag){
            //邏輯不完整
            throw new RuntimeException("邏輯不完整,數(shù)字計(jì)數(shù)器未執(zhí)行完畢");
        }
        //開(kāi)始執(zhí)行邏輯
        flag = true;
        //do something
        Thread.sleep(5000);
        number++;
        //執(zhí)行完畢
        flag=false;
        return number;
    }
}

事實(shí)上,在實(shí)際工作中這樣的方法可能需要執(zhí)行比較久的時(shí)間,所以這里我們通過(guò)調(diào)用Thread.sleep來(lái)模擬這個(gè)耗時(shí)操作。

這里我們還有一個(gè)flag參數(shù),來(lái)標(biāo)志這個(gè)increaseNumber方法是否成功執(zhí)行完畢。

好了,接下來(lái)我們?cè)谝粋€(gè)線程中調(diào)用這個(gè)類的方法,看看會(huì)發(fā)生什么:

    public static void main(String[] args) throws InterruptedException {
        NumberCounter numberCounter= new NumberCounter();
        Thread thread = new Thread(()->{
            while (true){
                try {
                    numberCounter.increaseNumber();
                } catch (InterruptedException e) {
                   e.printStackTrace();
                }
            }
        });
        thread.start();
        Thread.sleep(3000);
        thread.stop();
        numberCounter.increaseNumber();
    }

這里,我們創(chuàng)建了一個(gè)線程,等這個(gè)線程運(yùn)行3秒鐘之后,直接調(diào)用thread.stop方法,結(jié)果我們發(fā)現(xiàn)出現(xiàn)了下面的異常:

Exception in thread "main" java.lang.RuntimeException: 邏輯不完整,數(shù)字計(jì)數(shù)器未執(zhí)行完畢
    at com.flydean.NumberCounter.increaseNumber(NumberCounter.java:12)
    at com.flydean.Main.main(Main.java:18)

這是因?yàn)閠hread.stop方法直接終止了線程的運(yùn)行,導(dǎo)致mberCounter.increaseNumber未執(zhí)行完畢。

但是這個(gè)未執(zhí)行完畢的狀態(tài)是隱藏的,如果使用thread.stop方法來(lái)終止線程,很有可能導(dǎo)致未知的結(jié)果。

所以,我們說(shuō)thread.stop是不安全的。

怎么才能安全

那么,如果不調(diào)用thread.stop方法,怎么才能安全的終止線程呢?

所謂安全,那就是需要讓線程里面的邏輯執(zhí)行完畢,而不是執(zhí)行一半。

為了實(shí)現(xiàn)這個(gè)效果,Thread為我們提供了三個(gè)比較類似的方法,他們分別是interrupt、interrupted和isInterrupted。

interrupt是給線程設(shè)置中斷標(biāo)志;interrupted是檢測(cè)中斷并清除中斷狀態(tài);isInterrupted只檢測(cè)中斷。還有重要的一點(diǎn)就是interrupted是類方法,作用于當(dāng)前線程,interrupt和isInterrupted作用于此線程,即代碼中調(diào)用此方法的實(shí)例所代表的線程。

interrupt就是中斷的方法,它的工作流程如下:

  • 如果當(dāng)前線程實(shí)例在調(diào)用Object類的wait(),wait(long)或wait(long,int)方法或join(),join(long),join(long,int)方法,或者在該實(shí)例中調(diào)用了Thread.sleep(long)或Thread.sleep(long,int)方法,并且正在阻塞狀態(tài)中時(shí),則其中斷狀態(tài)將被清除,并將收到InterruptedException。
  • 如果此線程在InterruptibleChannel上的I/O操作中處于被阻塞狀態(tài),則該channel將被關(guān)閉,該線程的中斷狀態(tài)將被設(shè)置為true,并且該線程將收到j(luò)ava.nio.channels.ClosedByInterruptException異常。
  • 如果此線程在java.nio.channels.Selector中處于被被阻塞狀態(tài),則將設(shè)置該線程的中斷狀態(tài)為true,并且它將立即從select操作中返回。
  • 如果上面的情況都不成立,則設(shè)置中斷狀態(tài)為true。

在上面的例子中,NumberCounter的increaseNumber方法中,我們調(diào)用了Thread.sleep方法,所以如果在這個(gè)時(shí)候,調(diào)用了thread的interrupt方法,線程就會(huì)拋出一個(gè)InterruptedException異常。

我們把上面調(diào)用的例子改成下面這樣:

    public static void main(String[] args) throws InterruptedException {
        NumberCounter numberCounter = new NumberCounter();

        Thread thread = new Thread(() -> {
            while (true) {
                try {
                    numberCounter.increaseNumber();
                } catch (InterruptedException e) {
                    System.out.println("捕獲InterruptedException");
                    throw new RuntimeException(e);
                }
            }
        });

        thread.start();
        Thread.sleep(500);
        thread.interrupt();
        numberCounter.increaseNumber();
    }

運(yùn)行之后再試一次:

Exception in thread "main" Exception in thread "Thread-0" java.lang.RuntimeException: 邏輯不完整,數(shù)字計(jì)數(shù)器未執(zhí)行完畢
    at com.flydean.NumberCounter.increaseNumber(NumberCounter.java:12)
    at com.flydean.Main2.main(Main2.java:21)
java.lang.RuntimeException: java.lang.thread.interrupt: sleep interrupted
    at com.flydean.Main2.lambda$main$0(Main2.java:13)
    at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.InterruptedException: sleep interrupted
    at java.base/java.lang.Thread.sleep(Native Method)
    at com.flydean.NumberCounter.increaseNumber(NumberCounter.java:17)
    at com.flydean.Main2.lambda$main$0(Main2.java:10)
    ... 1 more
捕獲InterruptedException

可以看到,我們捕獲到了這個(gè)InterruptedException,并且得知具體的原因是sleep interrupted。

捕獲異常之后的處理

從上面的分析可以得知,thread.stop跟thread.interrupt的表現(xiàn)機(jī)制是不一樣的。thread.stop屬于悄悄終止,我們程序不知道,所以會(huì)導(dǎo)致數(shù)據(jù)不一致,從而產(chǎn)生一些未知的異常。

而thread.interrupt會(huì)顯示的拋出InterruptedException,當(dāng)我們捕捉到這個(gè)異常的時(shí)候,我們就知道線程里面的邏輯在執(zhí)行的過(guò)程中受到了外部作用的干擾,那么我們就可以執(zhí)行一些數(shù)據(jù)恢復(fù)或者數(shù)據(jù)校驗(yàn)的動(dòng)作。

在上面的代碼中,我們是捕獲到了這個(gè)異常,打印出異常日志,然后向上拋出一個(gè)RuntimeException。

正常情況下我們是需要在捕獲異常之后,進(jìn)行一些處理。

那么自己處理完這個(gè)異常之后,是不是就完美了呢?

答案是否定的。

因?yàn)槿绻覀冏约禾幚砹诉@個(gè)InterruptedException, 那么程序中其他部分如果有依賴這個(gè)InterruptedException的話,就可能會(huì)出現(xiàn)數(shù)據(jù)不一致的情況。

所以我們?cè)谧约禾幚硗闕nterruptedException之后,還需要再次拋出這個(gè)異常。

怎么拋出InterruptedException異常呢?

有兩種方式,第一種就是在調(diào)用Thread.interrupted()清除了中斷標(biāo)志之后立即拋出:

   if (Thread.interrupted())  // Clears interrupted status!
       throw new InterruptedException();

還有一種方式就是,在捕獲異常之后,調(diào)用Thread.currentThread().interrupt()再次中斷線程。

public void run () {
  try {
    while (true) {
      // do stuff
    }
  }catch (InterruptedException e) {
    LOGGER.log(Level.WARN, "Interrupted!", e);
    // Restore interrupted state...
    Thread.currentThread().interrupt();
  }
}

這兩種方式都能達(dá)到預(yù)想的效果。

總結(jié)

線程不能調(diào)用stop來(lái)終止主要是因?yàn)椴粫?huì)拋出異常,從而導(dǎo)致一些安全和數(shù)據(jù)不一致的問(wèn)題。所以,最好的方式就是調(diào)用interrupt方法來(lái)處理。

到此這篇關(guān)于一文揭曉如何在Java中終止一個(gè)線程的文章就介紹到這了,更多相關(guān)Java終止線程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 有關(guān)tomcat內(nèi)存溢出的完美解決方法

    有關(guān)tomcat內(nèi)存溢出的完美解決方法

    下面小編就為大家?guī)?lái)一篇有關(guān)tomcat內(nèi)存溢出的完美解決方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-05-05
  • SpringBoot集成支付寶沙箱支付(支付、退款)

    SpringBoot集成支付寶沙箱支付(支付、退款)

    這篇文章主要為大家詳細(xì)介紹了SpringBoot集成支付寶沙箱支付,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-08-08
  • Spring?AOPr如何打通兩個(gè)切面之間的通信

    Spring?AOPr如何打通兩個(gè)切面之間的通信

    本文主要介紹了Spring?AOPr如何打通兩個(gè)切面之間的通信,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-03-03
  • Java中的Optional類用法詳細(xì)講解

    Java中的Optional類用法詳細(xì)講解

    這篇文章詳細(xì)介紹了Java中Optional類的使用,包括創(chuàng)建Optional對(duì)象,Optional對(duì)象的常用方法,如get、orElse、orElseGet、orElseThrow等,以及Optional類結(jié)合Lambda表達(dá)式和StreamAPI的使用,需要的朋友可以參考下
    2024-10-10
  • Windows安裝兩個(gè)或多個(gè)JDK并實(shí)現(xiàn)自由切換的方法

    Windows安裝兩個(gè)或多個(gè)JDK并實(shí)現(xiàn)自由切換的方法

    最近新接手一個(gè)項(xiàng)目,啟動(dòng)的時(shí)候,發(fā)現(xiàn)有些jar和現(xiàn)在正在使用的JDK版本不一致,一直啟動(dòng)有問(wèn)題,想著就多裝一個(gè)JDK,由于為了保證java的運(yùn)行環(huán)境和編譯環(huán)境保持一致,就需要我們?cè)O(shè)置jdk的環(huán)境變量,所以本文給大家介紹了Windows安裝兩個(gè)或多個(gè)JDK并實(shí)現(xiàn)自由切換的方法
    2025-03-03
  • 全面解析Spring Security 過(guò)濾器鏈的機(jī)制和特性

    全面解析Spring Security 過(guò)濾器鏈的機(jī)制和特性

    這篇文章主要介紹了Spring Security 過(guò)濾器鏈的機(jī)制和特性,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-07-07
  • SpringBoot詳解整合Redis緩存方法

    SpringBoot詳解整合Redis緩存方法

    本文主要介紹了SpringBoot整合Redis緩存的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • Mybatis-Plus 全局配置無(wú)效的解決方案

    Mybatis-Plus 全局配置無(wú)效的解決方案

    這篇文章主要介紹了Mybatis-Plus 全局配置無(wú)效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • Java雙色球系統(tǒng)開(kāi)發(fā)詳解

    Java雙色球系統(tǒng)開(kāi)發(fā)詳解

    這篇文章主要為大家詳細(xì)介紹了Java雙色球系統(tǒng)的開(kāi)發(fā),超級(jí)簡(jiǎn)單的邏輯,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • SpringCloud超詳細(xì)講解微服務(wù)網(wǎng)關(guān)Zuul基礎(chǔ)

    SpringCloud超詳細(xì)講解微服務(wù)網(wǎng)關(guān)Zuul基礎(chǔ)

    這篇文章主要介紹了SpringCloud?Zuul微服務(wù)網(wǎng)關(guān),負(fù)載均衡,熔斷和限流,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-10-10

最新評(píng)論

铜川市| 周口市| 荆州市| 芦山县| 桃园市| 大洼县| 得荣县| 滨州市| 霍山县| 瑞安市| 太原市| 浪卡子县| 大连市| 巩留县| 商河县| 延津县| 江西省| 平遥县| 宝兴县| 五大连池市| 马关县| 清新县| 花莲县| 建瓯市| 张北县| 荆州市| 沈阳市| 黔江区| 灵台县| 锦州市| 虞城县| 佛山市| 合阳县| 新昌县| 保山市| 赤城县| 朝阳县| 航空| 芜湖市| 保康县| 和平县|