1. 序章

このチュートリアルでは、JavaのInterruptedExceptionについて説明します。 まず、イラスト付きのスレッドのライフサイクルを簡単に説明します。 次に、マルチスレッドアプリケーションで作業すると、InterruptedExceptionが発生する可能性があることを確認します。 最後に、この例外を処理する方法を説明します。

2. マルチスレッドの基本

割り込みについて説明する前に、マルチスレッドについて確認しましょう。 マルチスレッドは、2つ以上のスレッドを同時に実行するプロセスです。 Javaアプリケーションは、 main()メソッドに関連付けられたメインスレッドと呼ばれる単一のスレッドで始まります。 このメインスレッドは、他のスレッドを開始できます。

スレッドは軽量です。つまり、同じメモリスペースで実行されます。 したがって、彼らは彼らの間で簡単に通信することができます。 スレッドのライフサイクルを見てみましょう。

新しいスレッドを作成するとすぐに、NEW状態になります。 プログラムがstart()メソッドを使用してスレッドを開始するまで、この状態のままになります。

スレッドでstart()メソッドを呼び出すと、スレッドはRUNNABLE状態になります。 この状態のスレッドは、実行中であるか、実行の準備ができています。

スレッドがモニターロックを待機していて、他のスレッドによってロックされているコードにアクセスしようとすると、BLOCKED状態になります。

スレッドは、 wait()メソッドの呼び出しなど、さまざまなイベントによってWAITING状態にすることができます。 この状態では、スレッドは別のスレッドからの信号を待機しています。

スレッドが実行を終了するか、異常終了すると、TERMINATED状態になります。 スレッドは中断される可能性があり、スレッドが中断されると、InterruptedExceptionがスローされます。

次のセクションでは、 InterruptedException について詳しく説明し、それに対応する方法を学びます。

3. InterruptedExceptionとは何ですか?

InterruptedException は、スレッドが待機中、スリープ中、またはその他の方法で占有されているときにスレッドが中断された場合にスローされます。 つまり、一部のコードがスレッドで Interrupt()メソッドを呼び出しています。 これはでチェックされた例外であり、Javaでの多くのブロッキング操作でスローされる可能性があります。

3.1. 割り込み

割り込みシステムの目的は、スレッドが他のスレッドのタスク(時間のかかる可能性のあるタスク)を中断できるようにするための明確に定義されたフレームワークを提供することです。 割り込みについて考える良い方法は、実行中のスレッドを実際に中断するのではなく、次の都合の良い機会にスレッドが自分自身を中断するように要求するだけです。

3.2. ブロッキングおよび割り込み可能なメソッド

スレッドはいくつかの理由でブロックされる可能性があります: Thread.sleep ()からのウェイクアップの待機、ロックの取得の待機、I / Oの完了の待機、または別のスレッドでの計算、など。

InterruptedExceptionは通常、すべてのブロッキングメソッドによってスローされるため、処理して修正アクションを実行できます。 Javaには、InterruptedExceptionをスローするメソッドがいくつかあります。 これらには、 Thread.sleep() Thread.join() Objectクラスのwait()メソッド、および[いくつか例を挙げると、 BlockingQueueのX124X]put()および take()メソッド。

3.3. スレッドの割り込みメソッド

割り込みを処理するためのThreadクラスのいくつかの主要なメソッドを簡単に見てみましょう。

public void interrupt() { ... }
public boolean isInterrupted() { ... }
public static boolean interrupted() { ... }

Threadは、スレッドを中断するためのinterrupt()メソッドを提供し、スレッドが中断されたかどうかを照会するには、isInterrupted()メソッドを使用できます。 場合によっては、現在のスレッドが中断されているかどうかをテストし、中断されている場合は、すぐにこの例外をスローしたい場合があります。 ここでは、 interrupted()メソッドを使用できます。

if (Thread.interrupted()) {
    throw new InterruptedException();
}

3.4. 割り込みステータスフラグ

割り込みメカニズムは、割り込みステータスと呼ばれるフラグを使用して実装されます。 各スレッドには、中断されたステータスを表すブールプロパティがあります。 Thread.interrupt()を呼び出すと、このフラグが設定されます。 スレッドが静的メソッドThread.interrupted()を呼び出して割り込みをチェックすると、割り込みステータスがクリアされます。

割り込み要求に応答するには、InterruptedExceptionを処理する必要があります。次のセクションでその方法を説明します。

4. InterruptedExceptionの処理方法

スレッドのスケジューリングはJVMに依存することに注意することが重要です。 JVMは仮想マシンであり、マルチスレッドをサポートするにはネイティブオペレーティングシステムのリソースが必要なため、これは当然のことです。 したがって、スレッドが中断されないことを保証することはできません。

割り込みは、スレッドが実行していることを停止し、他のことを実行する必要があることをスレッドに示します。 具体的には、 Executor またはその他のスレッド管理メカニズムによって実行されるコードを記述している場合、コードが割り込みに迅速に応答することを確認する必要があります。 そうしないと、アプリケーションがデッドロックにつながる可能性があります。

InterruptedExceptionを処理するための実用的な戦略はほとんどありません。 それらを見てみましょう。

4.1. InterruptedExceptionを伝播します

InterruptedException がコールスタックに伝播できるようにすることができます。たとえば、は、各メソッドに順番にthrows句を追加し、呼び出し元に割り込みの処理方法を決定させることで実現できます。 これには、例外をキャッチしないこと、または例外をキャッチして再スローすることが含まれる場合があります。 例でこれを達成してみましょう:

public static void propagateException() throws InterruptedException {
    Thread.sleep(1000);
    Thread.currentThread().interrupt();
    if (Thread.interrupted()) {
        throw new InterruptedException();
    }
}

ここでは、スレッドが中断されているかどうかを確認しています。中断されている場合は、InterruptedExceptionをスローします。 それでは、 propagateException()メソッドを呼び出しましょう。

public static void main(String... args) throws InterruptedException {
    propagateException();
}

このコードを実行しようとすると、スタックトレースとともにInterruptedExceptionを受け取ります。

Exception in thread "main" java.lang.InterruptedException
    at com.baeldung.concurrent.interrupt.InterruptExample.propagateException(InterruptExample.java:16)
    at com.baeldung.concurrent.interrupt.InterruptExample.main(InterruptExample.java:7)

これは例外に対応するための最も賢明な方法ですが、コードが Runnable の一部である場合など、例外をスローできない場合があります。 この状況では、それをキャッチしてステータスを復元する必要があります。 このシナリオの処理方法については、次のセクションで説明します。

4.2. 割り込みを復元する

InterruptedException。を伝播できない場合があります。たとえば、タスクが Runnable によって定義されているか、チェックされた例外をスローしないメソッドをオーバーライドするとします。 そのような場合、中断を維持することができます。 これを行う標準的な方法は、中断されたステータスを復元することです。

interrupt()メソッドを再度呼び出して(フラグをtrueに戻します)、呼び出しスタックの上位のコードが割り込みが発行されたことを確認できます。たとえば、スレッドに割り込みを入れて、中断されたステータスにアクセスしてみてください。

public class InterruptExample extends Thread {
    public static Boolean restoreTheState() {
        InterruptExample thread1 = new InterruptExample();
        thread1.start();
        thread1.interrupt();
        return thread1.isInterrupted();
    }
}

そして、この割り込みを処理し、割り込みステータスを復元する run()メソッドは次のとおりです。

public void run() {
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();  //set the flag back to <code>true
 } }

最後に、ステータスをテストしましょう。

assertTrue(InterruptExample.restoreTheState());

Javaの例外はすべての例外的なケースと条件をカバーしますが、コードとビジネスロジックに固有の特定のカスタム例外をスローしたい場合があります。 ここで、割り込みを処理するためのカスタム例外を作成できます。 次のセクションで説明します。

4.3. カスタム例外処理

カスタム例外は、標準のJava例外の一部ではない属性とメソッドを追加する柔軟性を提供します。 したがって、状況に応じて、カスタムの方法で割り込みを処理することは完全に有効です

アプリケーションが割り込み要求を適切に処理できるようにするために、追加の作業を完了することができます。 たとえば、スレッドがスリープ中またはI / O操作を待機していて、割り込みを受け取った場合、スレッドを終了する前にリソースを閉じることができます。

CustomInterruptedExceptionというカスタムチェック例外を作成しましょう。

public class CustomInterruptedException extends Exception {
    CustomInterruptedException(String message) {
        super(message);
    }
}

スレッドが中断されたときにCustomInterruptedExceptionをスローできます

public static void throwCustomException() throws Exception {
    Thread.sleep(1000);
    Thread.currentThread().interrupt();
    if (Thread.interrupted()) {
        throw new CustomInterruptedException("This thread was interrupted");
    }
}

また、例外が正しいメッセージでスローされているかどうかを確認する方法も見てみましょう。

@Test
 public void whenThrowCustomException_thenContainsExpectedMessage() {
    Exception exception = assertThrows(CustomInterruptedException.class, () -> InterruptExample.throwCustomException());
    String expectedMessage = "This thread was interrupted";
    String actualMessage = exception.getMessage();

    assertTrue(actualMessage.contains(expectedMessage));
}

同様に、例外を処理して、中断されたステータスを復元できます

public static Boolean handleWithCustomException() throws CustomInterruptedException{
    try {
        Thread.sleep(1000);
        Thread.currentThread().interrupt();
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        throw new CustomInterruptedException("This thread was interrupted...");
    }
    return Thread.currentThread().isInterrupted();
}

中断されたステータスをチェックして、trueが返されることを確認することでコードをテストできます。

assertTrue(InterruptExample.handleWithCustomException());

5. 結論

このチュートリアルでは、InterruptedExceptionを処理するさまざまな方法を見ました。 正しく処理すれば、アプリケーションの応答性と堅牢性のバランスをとることができます。 また、いつものように、ここで使用されているコードスニペットは、GitHubから入手できます。