目次
Thread.stop が無効になっている謎
安全を確保する方法
捕获异常之后的处理
ホームページ 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 は 2 つのネイティブ メソッドであり、具体的な実装についてはここでは説明しません。

停止方法は非常に合理的で問題ないようです。では、なぜこの方法は安全ではないのでしょうか?

次に例を見てみましょう。

NumberCounter クラスを作成します。このクラスには、数値に 1 を加算するために使用される安全なメソッド増加番号があります:

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 を呼び出して、この時間のかかる操作をシミュレートします。

ここには、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: ロジックが不完全で、デジタル カウンターが実行されていません
com.flydean.NumberCounter.increaseNumber(NumberCounter.java:12)
com.flydean.Main.main(Main.java:18)

これはスレッドが原因です.stop メソッド スレッドの実行が直接終了されるため、mberCounter.increaseNumber は完了しません。

ただし、この未完了の状態は隠蔽されているため、thread.stop メソッドを使用してスレッドを終了すると、不明な結果が生じる可能性があります。

したがって、thread.stop は安全ではないと言えます。

安全を確保する方法

では、thread.stop メソッドが呼び出されない場合、スレッドを安全に終了するにはどうすればよいでしょうか?

いわゆる安全性とは、スレッド内のロジックが半分実行されるのではなく、完全に実行される必要があることを意味します。

この効果を実現するために、Thread は、interrupt、interrupted、isInterrupted という 3 つの同様のメソッドを提供します。

interrupt はスレッドの割り込みフラグを設定します。interrupted は割り込みを検出して割り込みステータスをクリアします。isInterrupted は割り込みを検出するだけです。もう 1 つの重要な点は、interrupted は現在のスレッドで動作するクラス メソッドであるということです。interrupt と isInterrupted はこのスレッド、つまり、コード内でこのメソッドを呼び出すインスタンスによって表されるスレッドで動作します。

interrupt は割り込みメソッドです。そのワークフローは次のとおりです:

  • 現在のスレッド インスタンスが wait()、wait(long)、または wait(long, int) メソッド、join()、join(long)、join(long, int) メソッド、または Thread.sleep(long) または Thread.sleep(long, int) メソッドがこのインスタンスで呼び出され、ブロック状態になっています。中断ステータスはクリアされ、InterruptedException が受信されます。

  • InterruptibleChannel での I/O 操作中にこのスレッドがブロックされた場合、チャネルは閉じられ、スレッドの割り込みステータスが true に設定され、スレッド A java.nio .channels.ClosedByInterruptException 例外が受信されます。

  • このスレッドが java.nio.channels.Selector でブロックされている場合、スレッドの割り込みステータスは true に設定され、選択操作からすぐに戻ります。

  • 上記の条件がいずれも当てはまらない場合は、割り込みステータスを true に設定します。

上記の例では、NumberCounter の増加数値メソッドで Thread.sleep メソッドを呼び出しているため、この時点でスレッドの割り込みメソッドが呼び出されると、スレッドは An をスローします。中断された例外。

上記の呼び出し例を次のように変更します:

    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 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Javaの完全数 Javaの完全数 Aug 30, 2024 pm 04:28 PM

Java における完全数のガイド。ここでは、定義、Java で完全数を確認する方法、コード実装の例について説明します。

ジャワのウェカ ジャワのウェカ 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 Stream Foreachから休憩または戻ってきますか? Java 8 Stream Foreachから休憩または戻ってきますか? Feb 07, 2025 pm 12:09 PM

Java 8は、Stream APIを導入し、データ収集を処理する強力で表現力のある方法を提供します。ただし、ストリームを使用する際の一般的な質問は次のとおりです。 従来のループにより、早期の中断やリターンが可能になりますが、StreamのForeachメソッドはこの方法を直接サポートしていません。この記事では、理由を説明し、ストリーム処理システムに早期終了を実装するための代替方法を調査します。 さらに読み取り:JavaストリームAPIの改善 ストリームを理解してください Foreachメソッドは、ストリーム内の各要素で1つの操作を実行する端末操作です。その設計意図はです

Java での日付までのタイムスタンプ Java での日付までのタイムスタンプ Aug 30, 2024 pm 04:28 PM

Java での日付までのタイムスタンプに関するガイド。ここでは、Java でタイムスタンプを日付に変換する方法とその概要について、例とともに説明します。

カプセルの量を見つけるためのJavaプログラム カプセルの量を見つけるためのJavaプログラム Feb 07, 2025 am 11:37 AM

カプセルは3次元の幾何学的図形で、両端にシリンダーと半球で構成されています。カプセルの体積は、シリンダーの体積と両端に半球の体積を追加することで計算できます。このチュートリアルでは、さまざまな方法を使用して、Javaの特定のカプセルの体積を計算する方法について説明します。 カプセルボリュームフォーミュラ カプセルボリュームの式は次のとおりです。 カプセル体積=円筒形の体積2つの半球体積 で、 R:半球の半径。 H:シリンダーの高さ(半球を除く)。 例1 入力 RADIUS = 5ユニット 高さ= 10単位 出力 ボリューム= 1570.8立方ユニット 説明する 式を使用してボリュームを計算します。 ボリューム=π×R2×H(4

Spring Tool Suiteで最初のSpring Bootアプリケーションを実行するにはどうすればよいですか? Spring Tool Suiteで最初のSpring Bootアプリケーションを実行するにはどうすればよいですか? Feb 07, 2025 pm 12:11 PM

Spring Bootは、Java開発に革命をもたらす堅牢でスケーラブルな、生産対応のJavaアプリケーションの作成を簡素化します。 スプリングエコシステムに固有の「構成に関する慣習」アプローチは、手動のセットアップを最小化します。

See all articles