Try/finallyセクションを持つ関数があり、それを実行しているスレッドがtryブロック内で中断された場合、中断が実際に発生する前にfinallyブロックが実行されますか?
Javaチュートリアル によると、 "try
またはcatch
コードを実行しているスレッドが中断または強制終了された場合、finally
ブロックは、アプリケーション全体が継続していても実行されない場合があります。」
これが完全な一節です:
finally
ブロックは、try
ブロックが終了すると常に実行されます。これにより、予期しない例外が発生した場合でも、finally
ブロックが確実に実行されます。ただし、finally
は、例外処理だけでなく、プログラマーがクリーンアップコードをreturn
、continue
、または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()を使用してフラグを復元することをお勧めします。これは、ネストされたシンクロナイザーを使用するより複雑な状況で発生します。たとえば、深くネストされたコンポーネントがスリープを中断し、クリアしたままにしておくと、上位層が中断を認識できなくなる可能性があります。ここにある他の回答のような単純なおもちゃの例では、フラグが復元されたかどうかは関係ありません。フラグを再度チェックするものはなく、スレッドは終了します。
Javaのスレッド割り込みはフラグを設定しているだけです。現在実行中のコードに特別なことが発生したり、制御の流れに影響を与えたりすることはありません。
スレッドがInterruptedExceptionをスローする操作に従事している、または開始しようとすると、そのメソッドが呼び出されたポイントから例外がスローされ、tryブロック内にある場合は、例外が通常どおり終了する前にfinallyが実行されます。 。
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;
}
中断の効果は、次にブロッキング操作が発生したときにInterruptedException
をスローすることです(実際には、次にInterruptedException
をスローできることを指定するメソッドが呼び出されたときに)、その時点で- -いつものように-通常のtry/catch
実行フローに従います。これは、実際にfinally
および該当するtry
esの後にcatch
ブロックを実行します。
中断前ではなく、tryブロックの他の例外と同じように実行されます。