web-dev-qa-db-ja.com

関数を実行しているスレッドが中断された場合、finallyブロックは実行されますか?

Try/finallyセクションを持つ関数があり、それを実行しているスレッドがtryブロック内で中断された場合、中断が実際に発生する前にfinallyブロックが実行されますか?

19
user940016

Javaチュートリアル によると、 "tryまたはcatchコードを実行しているスレッドが中断または強制終了された場合、finallyブロックは、アプリケーション全体が継続していても実行されない場合があります。」

これが完全な一節です:

finallyブロックは、tryブロックが終了すると常に実行されます。これにより、予期しない例外が発生した場合でも、finallyブロックが確実に実行されます。ただし、finallyは、例外処理だけでなく、プログラマーがクリーンアップコードをreturncontinue、またはbreakによって誤ってバイパスされることを回避するのに役立ちます。 。クリーンアップコードをfinallyブロックに配置することは、例外が予想されない場合でも、常に良い習慣です。

注:tryまたはcatchコードの実行中にJVMが終了すると、finallyブロックが実行されない場合があります。同様に、tryまたはcatchコードを実行しているスレッドが中断または強制終了された場合、アプリケーション全体が継続していても、finallyブロックは実行されない可能性があります。

class Thread1 implements Runnable {

    @Override
    public void run() {
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("finally executed");
        }
    }
}

.。

t1.start();
t1.interrupt();

出力します-最終的に実行されました

OracleのJavaチュートリアルの多くは役に立ちます(保護されたブロックのページとSAXの紹介を参照する回答があります)が、必ずしも信頼できるものではなく、一部には間違いがあるか、不完全です。引用質問で参照されているのは、JVMが終了すると中断が発生し、混乱を招きます。

まず、Javaのスレッド割り込みはOSレベルの割り込みとは関係ありません。 名前を共有すると混乱する可能性がありますが、接続はありません

次に、JVM出口は明らかに、クリーンアップを行う機会なしにスレッドを強制終了します。スレッドがfinallyブロックまで到達する前にプロセスが終了した場合は、残念です。しかし、中断との比較はありません。中断については、最終的にブロックが完了するのを妨げるものは何もありません。

割り込みの設計原則は、割り込みに作用するには、割り込みされるスレッドの協力が必要であるということです。中断されたスレッドはその裁量で応答します。中断によってスレッドに何かを強制することはありません。 Thread#interrupt()を呼び出すことはすべて、スレッドにフラグを設定することです。待機やスリープなどのブロック方法は、フラグをチェックして、早起きする必要があるかどうかを確認します。 (InterruptedExceptionはチェックされた例外であるため、誰がいつスローするかを知ることができ、Runnableはそれを計画できます。)また、任意のコードでThread#isInterrupted()を使用して、スレッドにフラグが設定されているかどうかを確認できます。

Thread#sleep()は、割り込みフラグが設定されていることを認識すると、InterruptedExceptionをスローする前にフラグをクリアします。スレッドがInterruptedExceptionをキャッチした場合、そのスレッドで実行されている他のコードが割り込みについて知る必要がある場合に備えて、Thread.currentThread()。interrupt()を使用してフラグを復元することをお勧めします。これは、ネストされたシンクロナイザーを使用するより複雑な状況で発生します。たとえば、深くネストされたコンポーネントがスリープを中断し、クリアしたままにしておくと、上位層が中断を認識できなくなる可能性があります。ここにある他の回答のような単純なおもちゃの例では、フラグが復元されたかどうかは関係ありません。フラグを再度チェックするものはなく、スレッドは終了します。

4
Nathan Hughes

Javaのスレッド割り込みはフラグを設定しているだけです。現在実行中のコードに特別なことが発生したり、制御の流れに影響を与えたりすることはありません。

スレッドがInterruptedExceptionをスローする操作に従事している、または開始しようとすると、そのメソッドが呼び出されたポイントから例外がスローされ、tryブロック内にある場合は、例外が通常どおり終了する前にfinallyが実行されます。 。

4
Affe

comments to answer で、@ Risadinhaは、finallyブロック内の中断フラグを復元した場合に、catchブロックのコードが実行されるかどうかについて非常に有効な質問をしました。 ] Thread.currentThread().interrupt()を呼び出してブロックします。

テストする小さなコードスニペットは次のとおりです。

final SomeContext context = new SomeContext();
Thread thread = new Thread() {
    @Override
    public void run() {
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            // this code gets executed even though
            // we interrupt thread in catch block.
            context.value = 9;  
        }
    }
};

thread.start();
thread.interrupt();

thread.join(); // need to wait for thread code to complete

assertEquals(context.value, 9); // values are the same!

SomeContextクラスコード:

class SomeContext {
    public volatile int value = 10;
}
3
Keenle

中断の効果は、次にブロッキング操作が発生したときにInterruptedExceptionをスローすることです(実際には、次にInterruptedExceptionをスローできることを指定するメソッドが呼び出されたときに)、その時点で- -いつものように-通常のtry/catch実行フローに従います。これは、実際にfinallyおよび該当するtryesの後にcatchブロックを実行します。

1
Louis Wasserman

中断前ではなく、tryブロックの他の例外と同じように実行されます。

0
jdb