Java でのスレッド中断の詳細な紹介

WBOY
リリース: 2022-11-09 13:56:44
転載
1194 人が閲覧しました

この記事では、java に関する関連知識を提供します。主にスレッドの中断に関する関連内容を紹介します。中断とは、スレッドが互いに中断する方法です。たとえば、スレッド a とスレッド b が 2 つあります。 a がある時点で b の実行を中断したい場合、 b.interrupt() メソッドを呼び出して中断することができます。一緒に見てみましょう。皆さんのお役に立てれば幸いです。

Java でのスレッド中断の詳細な紹介

推奨学習: 「java ビデオ チュートリアル

Java におけるスレッド中断

1 スレッド中断関連の方法はじめに

Java マルチスレッド プログラミングの interrupt() メソッド、isInterrupted() メソッド、および interrupted() メソッドはすべて、スレッド 割り込み関連のメソッドは非常に重要です。これら 3 つの方法は名前が非常に似ており、原理を理解していないと混同しやすいため、区別するためにここで紹介します。 interrupt() メソッドと isInterrupted() メソッドはどちらもインスタンス メソッド (非クラス静的メソッド) であるため、先頭に thread1 を追加しました。インスタンス化された特定のスレッド:

thread1.interrupt() メソッド

thread1.interrupt() メソッドは、スレッドを中断するために使用されます。この割り込みは、通常、割り込みとして理解できます。たとえば、2 つのスレッド ab があり、スレッド a が何らかの理由でスレッド b を中断したい場合、スレッド a b.interrupt() は内部的に呼び出すことができます。ただし、この実装はスレッド b割り込みステータス フラグ を設定することで実現されることに注意してください。 b スレッドコードの実行中、ループ本体内で 割り込みステータスマーク を継続的に判定することで、a の割り込み要求が実際に応答したかどうかを確認できます (実行の終了など)。

thread1.isInterrupted() メソッド

thread1.isInterrupted() メソッドは、スレッドの割り込みステータスを取得するために使用されます。たとえば、2 つのスレッド ab があり、スレッド a が何らかの理由でスレッド b を中断したい場合は、b を使用できます。 .interrupt()b に割り込みます。スレッド b 内では、その割り込みステータスと割り込みがあったかどうかを確認し、割り込みステータスに基づいて割り込み要求に応答するかどうか (現在のスレッドのループ本体の終了など) を確認できます。 )。 thread1.isInterrupted()native メソッドはメソッド内で直接呼び出されます。受信した ClearInterrupted パラメータは false です。割り込みステータスはクリアされません マーク:

public boolean isInterrupted() {
  return isInterrupted(false);
}// ClearInterrupted表示是否清楚中断状态标记private native boolean isInterrupted(boolean ClearInterrupted);
ログイン後にコピー

したがって、このメソッドを呼び出した後、割り込みフラグはクリアされません。

Thread.interrupted() メソッド

Thread.interrupted() は、Thread クラスで定義された静的メソッドです。は、現在の スレッドの割り込みステータス を決定するために使用されます。thread1.isInterrupted() とは異なり、このメソッドは、割り込みに戻った後、(リセット) をリセットします。割り込みステータス マーク、いわゆるリセットはデフォルト状態に戻すことであり、これは割り込みステータス マークをクリアするとも言えます。 Thread クラスのソース コードを見ると、Thread.interrupted() メソッドの実装が非常に簡単であることがわかります。native メソッドは内部で直接呼び出されます。ただし、ClearInterrupted渡されたパラメータは true で、これは割り込みステータス マークをクリアすることを意味します:

public static boolean interrupted() {
  return currentThread().isInterrupted(true);
}// ClearInterrupted表示是否清楚中断状态标记private native boolean isInterrupted(boolean ClearInterrupted);
ログイン後にコピー

thread1 であることがわかります。 isInterrupted()Thread.interrupted () の違いは、実際には、割り込みステータス フラグを取得した後にリセットするかどうかにあります。ニーズに合わせてご利用いただけます。

2 スレッドのブロックを考慮せずにスレッドを正常に停止する方法

スレッドの実行を正常に停止したい場合は、前に紹介した割り込みメカニズムが必要です。たとえば、2 つのスレッド

ab があります。スレッド a がスレッド b に割り込みたい場合、b.interrupt( )スレッド b の割り込みマークを設定して、スレッド b に通知するメソッド。スレッド b は、次の実装のように、いつでも他のスレッドからの割り込みに応答できるように、自身の割り込みマークを常にチェックする必要があります:

public class TestMain {
    public static void main(String[] args) throws InterruptedException {
        Thread b = new Thread(new Runnable() {
            @Override
            public void run() {
                int num = 0;
                while(true) {
                    if (Thread.interrupted()) {
                        break;
                    }
                    System.out.println("thread b running, num is:" + num++);
                }
            }
        });
        b.start();
        // 主线程sleep 1ms,让线程b循环一小会
        Thread.sleep(1);
        // 中断线程b
        b.interrupt();
    }
}
ログイン後にコピー
これは

メイン スレッド#内にあります。 ## 新しいスレッド b が作成されます。スレッド b の内部がループ本体です。各ループの開始時に、割り込みステータスがチェックされ、割り込みが発生しているかどうかが確認されます。中断された場合は、ループを終了してスレッドを終了します。メインスレッドは 1ms スリープし、最初にスレッド b を数回ループさせます。次に、メインスレッドは b.interrupt() を通じてスレッド b を中断します。プログラムを実行すると、次の出力が表示されます (結果はマシンによって異なります)。

thread b running, num is:0thread b running, num is:1thread b running, num is:2...
thread b running, num is:25thread b running, num is:26thread b running, num is:27Process finished with exit code 0
ログイン後にコピー

可以看到主线程成功的中断了线程b。当然,主线程之所以能成功的中断线程b,是因为线程b一直在检查自己的中断状态(如果线程b太自我,不考虑其他线程,只考虑自己运行,那主线程就无法成功打断线程b了)。

3 考虑线程阻塞时如何优雅的停止一个线程

前面我们成功的在主线程中中断了线程b,然后如果线程b中存在阻塞,比如下面的代码所示,线程b在sleep时被主线程中断:

public class TestMain {
    public static void main(String[] args) throws InterruptedException {
        Thread b = new Thread(new Runnable() {
            @Override
            public void run() {
                int num = 0;
                while(true) {
                    if (Thread.interrupted()) {
                        break;
                    }
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // Thread.currentThread().interrupt();
                        e.printStackTrace();
                    }
                    System.out.println("thread b running, num is:" + num++);
                }
            }
        });
        b.start();
        // 主线程sleep5.5秒,让线程b循环5次
        Thread.sleep(5500);
        // 中断线程b
        b.interrupt();
    }
}
ログイン後にコピー

这时线程b会抛出InterruptedException异常,上面的代码中我们仅仅打印了下该异常,相当于什么都没做。运行该代码结果如下:

thread b running, num is:0thread b running, num is:1thread b running, num is:2thread b running, num is:3thread b running, num is:4java.lang.InterruptedException: sleep interrupted
  at java.lang.Thread.sleep(Native Method)
  at test.TestMain$1.run(TestMain.java:25)
  at java.lang.Thread.run(Thread.java:748)
thread b running, num is:5thread b running, num is:6thread b running, num is:7thread b running, num is:8thread b running, num is:9...
ログイン後にコピー

可以看出,主线程未能成功中断线程b

3.1 InterruptedException异常介绍

线程内调用waitjoinsleep时都会进入阻塞状态。当线程处于阻塞状态时被中断,这时线程就会抛出InterruptedException异常,其实大家可以通俗的理解为一种通知即可。以sleep方法为例,大家可以按如下模拟实现来理解(底层是native实现):

public static void sleep(long millis) throws InterruptedException 
{
    while (/* still waiting for millis to become zero */) 
    {
        if (Thread.interrupted())
        {
            throw new InterruptedException();
        }
        // Keep waiting
    }
}
ログイン後にコピー

有了InterruptedException异常通知,线程就可以在阻塞时立即知道被中断了,进而采取一定的措施响应中断。需要注意的一点是,由于抛出了InterruptedException异常,因此不会在设置中断标志位。

3.2 考虑线程阻塞时如何优雅的停止一个线程

理解了InterruptedException异常,我们就可以在线程即使发生阻塞时也能成功进行中断了,如下所示:

public class TestMain {
    public static void main(String[] args) throws InterruptedException {
        Thread b = new Thread(new Runnable() {
            @Override
            public void run() {
                int num = 0;
                while(true) {
                    if (Thread.interrupted()) {
                        break;
                    }
                    try {
                        Thread.sleep(1000); // 用sleep来模拟线程的执行
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt(); // 注意这里是重点!
                    }
                    System.out.println("thread b running, num is:" + num++);
                }
            }
        });
        b.start();
        // 主线程sleep5.5秒,让线程b先循环5次
        Thread.sleep(5500);
        // 中断线程b
        b.interrupt();
    }
}
ログイン後にコピー

这里我们在检查到InterruptedException异常时,重新设置了中断标志位,这样下次循环一开始时,即可判断被中断了,进而退出循环体。当然我们可以在InterruptedException异常catch时直接退出。

4 总结

我们介绍了Java中的线程中断相关知识点,通俗来讲,大家可以理解为中断就是一种线程间相互打断的一种方式,比如两个线程aba如果在某一时刻想打断b的执行,则可以调用b.interrupt()方法进行中断,但是要注意,这里仅仅是设置b的中断状态位,b看到中断状态位后可以自行决定是否响应,当然,正常情况我们写的代码都需要做好中断状态位的判断(这一点大家在写业务代码时确实经常忽略)。另外对于阻塞中的线程,Java通过InterruptedException异常来进行通知。

推荐学习:《java视频教程

以上がJava でのスレッド中断の詳細な紹介の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:juejin.im
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート