目錄
Thread.stop被禁用之謎
但是這個未執行完畢的狀態是隱藏的,如果使用thread.stop方法來終止線程,很有可能導致未知的結果。
捕获异常之后的处理
首頁 Java java教程 怎麼在Java中終止一個線程

怎麼在Java中終止一個線程

Apr 26, 2023 am 08:58 AM
java

Thread.stop被禁用之謎

問道怎麼終止一個線程,可能大多數人都知道可以呼叫Thread.stop方法。

但這個方法從jdk1.2之後就不建議使用了,為什麼不建議使用呢?

我們先來看下這個方法的定義:

  @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這個方法首先偵測有沒有執行緒存取的權限。如果有權限的話,來判斷當前的線程是否是剛剛創建的線程,如果不是剛剛創建的,那麼就調用resume方法來解除線程的暫停狀態。

最後呼叫stop0方法來結束執行緒。

其中resume和stop0是兩個native的方法,具體的實作這裡就不講了。

看起來stop方法很合理,沒有什麼問題。那為什麼說這個方法是不安全的呢?

接下來我們來看一個例子。

我們建立一個NumberCounter的類,這個類別有一個increaseNumber的安全方法,用來對number加一:

public class NumberCounter {
    //要保存的数字
    private volatile int number=0;
    //数字计数器的逻辑是否完整
    private volatile boolean flag = false;

    public synchronized int increaseNumber() throws InterruptedException {
        if(flag){
            //逻辑不完整
            throw new RuntimeException("逻辑不完整,数字计数器未执行完毕");
        }
        //开始执行逻辑
        flag = true;
        //do something
        Thread.sleep(5000);
        number++;
        //执行完毕
        flag=false;
        return number;
    }
}
登入後複製

事實上,在實際工作中這樣的方法可能需要執行比較久的時間,所以這裡我們透過呼叫Thread.sleep來模擬這個耗時操作。

這裡我們還有一個flag參數,來標誌這個increaseNumber方法是否成功執行完畢。

好了,接下來我們在一個線程中調用這個類別的方法,看看會發生什麼:

    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();
    }
登入後複製

這裡,我們創建了一個線程,等這個線程運行3秒鐘之後,直接呼叫thread.stop方法,結果我們發現出現了下面的例外:

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

##.stop方法方法方法方法直接終止了執行緒的運行,導致mberCounter.increaseNumber未執行完畢。

但是這個未執行完畢的狀態是隱藏的,如果使用thread.stop方法來終止線程,很有可能導致未知的結果。

所以,我們說thread.stop是不安全的。

怎麼才能安全

那麼,如果不呼叫thread.stop方法,怎麼才能安全的終止執行緒呢?

所謂安全,就是需要讓執行緒裡面的邏輯執行完畢,而不是執行一半。

為了達到這個效果,Thread為我們提供了三個比較類似的方法,他們分別是interrupt、interrupted和isInterrupted。

    interrupt是為執行緒設定中斷標誌;interrupted是偵測中斷並清除中斷狀態;isInterrupted只偵測中斷。還有重要的一點就是interrupted是類別方法,作用於目前線程,interrupt和isInterrupted作用於此線程,也就是程式碼中呼叫此方法的實例所代表的執行緒。
  • interrupt就是中斷的方法,它的工作流程如下:

  • #如果目前執行緒實例在呼叫Object類別的wait(),wait(長)或wait( long,int)方法或join(),join(long),join(long,int)方法,或在該實例中呼叫了Thread.sleep(long)或Thread.sleep(long,int)方法,並且正在阻塞狀態中時,則其中斷狀態將被清除,並將收到InterruptedException。

  • 如果此執行緒在InterruptibleChannel上的I/O操作中處於被阻塞​​狀態,則該channel將關閉,該執行緒的中斷狀態將被設為true,並且該執行緒將收到java.nio.channels.ClosedByInterruptException異常。

  • 如果此執行緒在java.nio.channels.Selector中處於被阻塞​​狀態,則會設定該執行緒的中斷狀態為true,並且它將立即從select操作中傳回。

如果上面的情況都不成立,則設定中斷狀態為true。

在上面的範例中,NumberCounter的increaseNumber方法中,我們呼叫了Thread.sleep方法,所以如果在這個時候,呼叫了thread的interrupt方法,執行緒就會拋出一個InterruptedException異常。

###我們把上面呼叫的例子改成下面這樣:###
    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();
    }
登入後複製
###運行之後再試一次:###

Exception in thread "main" Exception in thread "Thread-0" java.lang.RuntimeException: 逻辑不完整,数字计数器未执行完毕
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

可以看到,我们捕获到了这个InterruptedException,并且得知具体的原因是sleep interrupted。

捕获异常之后的处理

从上面的分析可以得知,thread.stop跟thread.interrupt的表现机制是不一样的。thread.stop属于悄悄终止,我们程序不知道,所以会导致数据不一致,从而产生一些未知的异常。

而thread.interrupt会显示的抛出InterruptedException,当我们捕捉到这个异常的时候,我们就知道线程里面的逻辑在执行的过程中受到了外部作用的干扰,那么我们就可以执行一些数据恢复或者数据校验的动作。

在上面的代码中,我们是捕获到了这个异常,打印出异常日志,然后向上抛出一个RuntimeException。

正常情况下我们是需要在捕获异常之后,进行一些处理。

那么自己处理完这个异常之后,是不是就完美了呢?

答案是否定的。

因为如果我们自己处理了这个InterruptedException, 那么程序中其他部分如果有依赖这个InterruptedException的话,就可能会出现数据不一致的情况。

所以我们在自己处理完InterruptedException之后,还需要再次抛出这个异常。

怎么抛出InterruptedException异常呢?

有两种方式,第一种就是在调用Thread.interrupted()清除了中断标志之后立即抛出:

   if (Thread.interrupted())  // Clears interrupted status!
       throw new InterruptedException();
登入後複製

还有一种方式就是,在捕获异常之后,调用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();
  }
}
登入後複製

这两种方式都能达到预想的效果。

以上是怎麼在Java中終止一個線程的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

Java 中的平方根 Java 中的平方根 Aug 30, 2024 pm 04:26 PM

Java 中的平方根指南。下面我們分別透過例子和程式碼實作來討論平方根在Java中的工作原理。

Java 中的完美數 Java 中的完美數 Aug 30, 2024 pm 04:28 PM

Java 完美數指南。這裡我們討論定義,如何在 Java 中檢查完美數?

Java 中的阿姆斯壯數 Java 中的阿姆斯壯數 Aug 30, 2024 pm 04:26 PM

Java 中的阿姆斯壯數指南。這裡我們討論一下java中阿姆斯壯數的介紹以及一些程式碼。

Java 中的隨機數產生器 Java 中的隨機數產生器 Aug 30, 2024 pm 04:27 PM

Java 隨機數產生器指南。在這裡,我們透過範例討論 Java 中的函數,並透過範例討論兩個不同的生成器。

Java中的Weka Java中的Weka Aug 30, 2024 pm 04:28 PM

Java 版 Weka 指南。這裡我們透過範例討論簡介、如何使用 weka java、平台類型和優點。

Java 中的史密斯數 Java 中的史密斯數 Aug 30, 2024 pm 04:28 PM

Java 史密斯數指南。這裡我們討論定義,如何在Java中檢查史密斯號?帶有程式碼實現的範例。

Java Spring 面試題 Java Spring 面試題 Aug 30, 2024 pm 04:29 PM

在本文中,我們保留了最常被問到的 Java Spring 面試問題及其詳細答案。這樣你就可以順利通過面試。

突破或從Java 8流返回? 突破或從Java 8流返回? Feb 07, 2025 pm 12:09 PM

Java 8引入了Stream API,提供了一種強大且表達力豐富的處理數據集合的方式。然而,使用Stream時,一個常見問題是:如何從forEach操作中中斷或返回? 傳統循環允許提前中斷或返回,但Stream的forEach方法並不直接支持這種方式。本文將解釋原因,並探討在Stream處理系統中實現提前終止的替代方法。 延伸閱讀: Java Stream API改進 理解Stream forEach forEach方法是一個終端操作,它對Stream中的每個元素執行一個操作。它的設計意圖是處

See all articles