web-dev-qa-db-ja.com

JavaでのInterruptedExceptionの処理

InterruptedExceptionを処理する次の方法の違いは何ですか?それを行う最良の方法は何ですか?

try{
 //...
} catch(InterruptedException e) { 
   Thread.currentThread().interrupt(); 
}

OR

try{
 //...
} catch(InterruptedException e) {
   throw new RuntimeException(e);
}

編集:これらの2つがどのシナリオで使用されているかも知りたいです。

259
devnull

InterruptedExceptionを処理する次の方法の違いは何ですか?それを行う最良の方法は何ですか?

InterruptedExceptionをスローするメソッドを呼び出したので、おそらくこの質問をするようになるでしょう。

まず、throws InterruptedExceptionが何であるかを確認する必要があります。メソッドシグネチャの一部と、呼び出しているメソッドを呼び出した結果の可能性。そのため、InterruptedExceptionはメソッド呼び出しの完全に有効な結果であるという事実を受け入れることから始めます。

今、あなたが呼び出しているメソッドがそのような例外をスローする場合、yourメソッドは何をすべきですか?次のことを考えて答えを理解できます。

メソッドにとって意味がありますかyouInterruptedExceptionをスローするように実装していますInterruptedExceptionyourメソッドを呼び出したときの賢明な結果?

  • yesの場合、throws InterruptedExceptionyourメソッドシグネチャの一部である必要があり、例外を伝播させる必要があります(つまり、まったくキャッチしません)。

    :メソッドは、ネットワークからの値を待って計算を終了し、結果を返します。ブロッキングネットワーク呼び出しがInterruptedExceptionをスローする場合、メソッドは通常の方法で計算を終了できません。 InterruptedExceptionを伝播させます。

    int computeSum(Server server) throws InterruptedException {
        // Any InterruptedException thrown below is propagated
        int a = server.getValueA();
        int b = server.getValueB();
        return a + b;
    }
    
  • noの場合、throws InterruptedExceptionを使用してメソッドを宣言しないでください。例外をキャッチする必要があります。この状況では、次の2つの点に注意する必要があります。

    1. 誰かがあなたのスレッドを中断しました。おそらく誰かが操作をキャンセルしたり、プログラムを正常に終了したりすることを望んでいます。あなたはその人に礼儀正しく、さらなる苦労なしにあなたの方法から戻るべきです。

    2. yourメソッドは、InterruptedExceptionの場合に適切な戻り値を生成することができますが、スレッドが中断されたという事実は依然として重要です。特に、メソッドを呼び出すコードは、メソッドの実行中に割り込みが発生したかどうかに関心がある場合があります。したがって、中断フラグを設定して、中断が発生したという事実をログに記録する必要があります:Thread.currentThread().interrupt()

    :ユーザーは2つの値の合計を印刷するように要求しました。 「Failed to compute sum」の印刷は、合計を計算できない場合は許容できます(InterruptedExceptionによるスタックトレースでプログラムをクラッシュさせるよりもはるかに優れています)。つまり、このメソッドをthrows InterruptedExceptionで宣言することはnot意味があります。

    void printSum(Server server) {
         try {
             int sum = computeSum(server);
             System.out.println("Sum: " + sum);
         } catch (InterruptedException e) {
             Thread.currentThread().interrupt();  // set interrupt flag
             System.out.println("Failed to compute sum");
         }
    }
    

今では、throw new RuntimeException(e)を実行するだけでは悪い考えであることは明らかです。発信者にあまり丁寧ではありません。新しいランタイム例外を作成することもできますが、根本的な原因(スレッドに実行を停止させたい人)が失われる可能性があります。

他の例:

Runnableの実装:お気づきかもしれませんが、Runnable.runのシグネチャはInterruptedExceptionsの再スローを許可していません。さて、youRunnableの実装にサインアップしました。つまり、youInterruptedExceptionsの可能性に対処するためにサインアップしました。 Callable などの別のインターフェースを選択するか、上記の2番目のアプローチに従います。

Thread.sleepの呼び出し:ファイルを読み取ろうとしていますが、1秒の間隔で10回試行する必要があります。 Thread.sleep(1000)を呼び出します。したがって、InterruptedExceptionを処理する必要があります。 tryToReadFileなどのメソッドの場合、「中断された場合、ファイルを読み取ろうとするアクションを完了できません」と言うのは完全に理にかなっています。言い換えると、メソッドがInterruptedExceptionsをスローすることは完全に理にかなっています。

String tryToReadFile(File f) throws InterruptedException {
    for (int i = 0; i < 10; i++) {
        if (f.exists())
            return readFile(f);
        Thread.sleep(1000);
    }
    return null;
}

この投稿は記事として書き直されました here

366
aioobe

たまたま私は今朝、ブライアン・ゲッツによる Java並行性の実践 で仕事をする方法について読んでいたところです。基本的に彼は、あなたが2つのことのうちの1つをするべきだと言います

  1. InterruptedExceptionを伝播します-メソッドを宣言して、チェックされたInterruptedExceptionをスローして、呼び出し元が処理するようにします。

  2. 割り込みを復元する-InterruptedExceptionをスローできないことがあります。これらの場合、InterruptedExceptionをキャッチし、currentThreadinterrupt()メソッドを呼び出して割り込みステータスを復元し、呼び出しスタックの上位のコードで割り込みが発行されたことを確認する必要があります。

85
mR_fr0g

あなたは何をしようとしているのですか?

InterruptedExceptionは、スレッドが待機中またはスリープ中のときにスローされ、別のスレッドがクラスinterruptThreadメソッドを使用してスレッドを中断します。したがって、この例外をキャッチすると、スレッドが中断されたことを意味します。他の場所からスレッドの「中断」状態を確認したい場合を除き、通常、Thread.currentThread().interrupt();を再度呼び出すことは意味がありません。

RuntimeExceptionをスローする他のオプションについては、実行するのが賢明なことではないようです(誰がこれをキャッチしますか?それをどのように処理しますか?)。

17
Grodriguez

私にとってこれについての重要なことは、InterruptedExceptionが問題になることではなく、スレッドがあなたが指示したことを実行することです。したがって、RuntimeExceptionにラップして再スローするのは意味がありません。

多くの場合、RuntimeExceptionにラップされた例外を再スローするのは理にかなっています。ここで何が間違っていたのかわからず、修正することもできません。現在の処理フローから抜け出すだけです。アプリケーション全体の例外ハンドラをヒットして、ログに記録できるようにします。これは、InterruptedExceptionの場合ではなく、interrupt()が呼び出されたことに応答するスレッドであり、タイムリーにスレッドの処理をキャンセルするためにInterruptedExceptionをスローしています。

したがって、InterruptedExceptionを伝達するか、インテリジェントに(意図したとおりの処理を実行した場所で)実行し、割り込みフラグをリセットします。 InterruptedExceptionがスローされると、割り込みフラグがクリアされることに注意してください。 Jdkライブラリ開発者が行う前提は、例外をキャッチすると処理することになるため、デフォルトでフラグはクリアされます。

したがって、間違いなく最初の方法の方が良いです、質問で2番目に投稿された例は、スレッドが実際に中断されることを期待しない限り有用ではなく、中断するとエラーになります。

ここに私が書いた答えがあります 例で割り込みの仕組みを説明します 。例のコードで、InterruptedExceptionを使用してRunnableのrunメソッドのwhileループから抜け出していることがわかります。

12
Nathan Hughes

正しいデフォルトの選択は、あなたのスローリストにInterruptedExceptionを追加することです。割り込みは、別のスレッドがスレッドの終了を望んでいることを示します。このリクエストの理由は明らかにされておらず、完全にコンテキストに基づいているため、追加の知識がない場合は単なるシャットダウンであると想定する必要があり、シャットダウンを回避するものは非友好的な応答です。

JavaがInterruptedExceptionをランダムにスローすることはありません。すべてのアドバイスがアプリケーションに影響することはありませんが、開発者が「飲み込む」戦略に従うことが非常に不便になる場合があります。チームは多数のテストを開発し、Thread.Sleepを多く使用していました。 CIサーバーでテストの実行を開始しましたが、コードの欠陥が原因で永続的な待機が発生する場合がありました。状況を悪化させるために、CIジョブをキャンセルしようとしても、テストを中止することを意図したThread.Interruptがジョブを中止しなかったため、CIジョブは閉じられませんでした。ボックスにログインし、プロセスを手動で強制終了する必要がありました。

要するに、InterruptedExceptionを単にスローした場合、スレッドを終了するというデフォルトの意図に一致しています。スローリストにInterruptedExceptionを追加できない場合は、RuntimeExceptionでラップします。

InterruptedException自体がRuntimeExceptionである必要があるという非常に合理的な議論があります。これにより、「デフォルト」の処理が改善されるためです。 RuntimeExceptionはコード内のエラーを表すべきであるというカテゴリルールに固執しているため、RuntimeExceptionではありません。 InterruptedExceptionはコードのエラーから直接発生するものではないため、発生しません。しかし、実際には、コードにエラーがあるため(つまり、無限ループ、デッドロック)、InterruptedExceptionが頻繁に発生し、割り込みはそのエラーを処理するための他のスレッドのメソッドです。

合理的なクリーンアップが行われていることがわかっている場合は、実行してください。割り込みのより深い原因がわかっている場合は、より包括的な処理を行うことができます。

要約すると、処理の選択は次のリストに従う必要があります。

  1. デフォルトでは、スローに追加します。
  2. スローへの追加が許可されていない場合は、RuntimeException(e)をスローします。 (複数の悪いオプションの最良の選択)
  3. 割り込みの明示的な原因がわかっている場合にのみ、必要に応じて処理してください。処理がメソッドに対してローカルである場合、Thread.currentThread()。interrupt()の呼び出しによって中断されたリセット。
8
nedruod

ほとんどの人や記事が言及しているものに最後のオプションを追加したかっただけです。 mR_fr0gが述べているように、次のいずれかの方法で割り込みを正しく処理することが重要です。

  • InterruptExceptionの伝播

  • スレッドの割り込み状態を復元

またはさらに:

  • 割り込みのカスタム処理

状況に応じてカスタムの方法で割り込みを処理しても問題はありません。割り込みは強制的なコマンドとは異なり、終了の要求であるため、アプリケーションが要求を適切に処理できるように追加の作業を完了することは完全に有効です。たとえば、スレッドがスリープし、IOまたはハードウェアの応答を待っている場合、割り込みを受信すると、スレッドを終了する前に接続を正常に閉じることは完全に有効です。

このトピックを理解することを強くお勧めしますが、この記事は情報源として優れています。 http://www.ibm.com/developerworks/Java/library/j-jtp05236/

3
TheIT

場合によっては何もしなくても大丈夫だと思います。おそらくデフォルトで行うべきことではありませんが、割り込みを発生させる方法がない場合は、他に何をすべきかわかりません(おそらくエラーをログに記録しますが、プログラムフローには影響しません)。

1つのケースは、タスク(ブロッキング)キューがある場合です。これらのタスクを処理するデーモンスレッドがあり、自分でスレッドを中断しない場合(私の知る限り、jvmはjvmシャットダウン時にデーモンスレッドを中断しません)、割り込みが発生する方法がありません。ただ無視されます。 (デーモンスレッドはいつでもjvmによって強制終了される可能性があることを知っているため、場合によっては不適切です)。

編集:別のケースは、少なくともOracleのチュートリアルに基づいて、保護されたブロックである可能性があります: http://docs.Oracle.com/javase/tutorial/essential/concurrency/guardmeth.html

0
Bjarne Boström