JavaでのInterruptedExceptionの処理


317

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

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

または

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

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


回答:


436

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

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

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

さて、あなたが呼び出しているメソッドがそのような例外を投げたら、あなたのメソッドは何をすべきでしょうか?次のことを考えると、答えを見つけることができます。

あなたが実装しているメソッドスローすることは意味がありますInterruptedExceptionか?別の言い方InterruptedExceptionすれば、メソッドを呼び出すときに賢明な結果ですか?

  • 場合ははい、その後throws InterruptedExceptionの一部である必要があり、あなたのメソッドシグネチャ、あなたは(つまり、すべてでそれをキャッチしない)例外伝播をさせてください。

    :メソッドは、ネットワークからの値を待って計算を終了し、結果を返します。ブロッキングネットワークコールがスローした場合、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. にもかかわらず、あなたの方法がある場合には賢明な戻り値を生成するために管理することができInterruptedException、スレッドが中断されたという事実は、まだ重要であり得ます。特に、メソッドを呼び出すコードは、メソッドの実行中に割り込みが発生したかどうかに関係する場合があります。したがって、interruptedフラグを設定して、中断が発生したという事実をログに記録する必要があります。Thread.currentThread().interrupt()

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

    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)は、やることだけが悪い考えであることは明らかです。発信者にとってはあまり礼儀正しくありません。新しいランタイム例外を作成することもできますが、根本原因(誰かがスレッドの実行を停止したい)が失われる可能性があります。

その他の例:

実装RunnableRunnable.runご存知かもしれませんが、のシグネチャは再スローを許可していませんInterruptedExceptions。さて、あなたは実装Runnableにサインアップしました、それはあなたが可能なことに対処するためにサインアップしたことを意味しますInterruptedExceptions。などの別のインターフェースを選択するか、Callable上記の2番目の方法に従います。

 

呼び出しThread.sleep:ファイルを読み取ろうとしていますが、仕様では1秒間隔で10回試行する必要があると記載されています。あなたが電話しますThread.sleep(1000)。したがって、に対処する必要がありますInterruptedException。以下のような方法のためにtryToReadFile、それは言って、完璧な理にかなって、「私は、中断していた場合、私は、ファイルを読み込むしようとしているの私のアクションを完了できません」。つまり、メソッドがthrowすることは完全に理にかなってい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;
}

この投稿は、こちらの記事に書き直されました。


2番目の方法の問題は何ですか?
blitzkriegz

4
この記事ではinterrupt()、中断されたステータスを保持するために呼び出す必要があると述べています。これを行わない理由は何Thread.sleep()ですか?
oksayt

7
同意しません。スレッドが割り込みをサポートしていない場合、受信された割り込みは予期しない状態(つまり、プログラミングエラー、不正なAPI使用)を示し、RuntimeExceptionでのベイルアウトは適切な応答です(fail-fast )。スレッドの一般規約の一部と見なされない限り、割り込みをサポートしていなくても、割り込みを受け取ったときに操作を継続する必要があります。
amoe 2013

12
InterruptedExceptionをスローし、「割り込みをサポートしていない」と言うメソッドを呼び出すと、ずさんなプログラミングのようになり、バグが発生するのを待っているように見えます。別の言い方をすると、InterruptedExceptionをスローするように宣言されているメソッドを呼び出す場合、そのメソッドが実際にそのような例外をスローしても、予期しない状態であってなりませ
aioobe

1
@MartiNito、いいえ、catchブロックを呼び出すThread.currentThread.interrupt()InterruptedException、割り込みフラグのみが設定されます。別のブロックがInterruptedException外側のInterruptedExceptionキャッチブロックでスロー/キャッチされることはありません。
aioobe

97

たまたま今朝、ブライアンゲッツによるJava Concurrency In Practiceに取り組む途中、今朝読んでいたところです。基本的に彼はあなたが3つのことの1つをすべきだと言います

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

  2. 割り込みを元に戻す -スローできない場合がありますInterruptedException。これらの場合InterruptedException、でinterrupt()メソッドを呼び出してをキャッチし、割り込みステータスを復元しcurrentThreadて、コールスタックの上位のコードが割り込みが発行されたことを確認し、メソッドからすばやく戻るようにする必要があります。注:これは、メソッドに「試行」または「ベストエフォート」のセマンティクスがある場合にのみ適用されます。つまり、メソッドが目的を達成しない場合、重大なことは何も起こりません。たとえば、log()またはそのsendMetric()ようなメソッドである場合boolean tryTransferMoney()がありvoid transferMoney()ます。詳細はこちらをご覧ください。

  3. メソッド内の割り込みを無視しますが、終了時にステータスを復元します(例:Guava経由)UninterruptiblesUninterruptiblesJCIP§7.1.3のキャンセルできないタスクの例のように、ボイラープレートコードを引き継ぎます。

10
「InterruptedExceptionをスローできない場合があります」 - メソッドがInterruptedExceptionを伝播することが適切ない場合があります。あなたの定式化は、できる限りInterruptedExceptionを再スローする必要があることを示唆しているようです。
aioobe 2014年

1
また、第7章を読むと、リスト7.7のように、キャンセルできないタスクがすぐに割り込みを復元するのではなく、完了後にのみ、これには微妙な点があることがわかります。また、Goetzにはその本の多くの共著者がいます...
Fizz

1
簡潔であるので、私はあなたの答えが好きですが、2番目のケースでは、関数から穏やかかつ迅速に戻ることも述べる必要があると思います。真ん中にThread.sleep()がある長い(または無限の)ループを想像してください。InterruptedExceptionのキャッチはThread.currentThread()。interrupt()呼び出し、ループを抜けます。
Yann Vo

@YannVo私はあなたの提案を適用するために答えを編集しました。
leventov

18

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

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

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


14
呼び出しThread.currentThread().interrupt()は、中断されたフラグを(再び)設定します。これは、割り込みが確実に通知され、より高いレベルで処理されることを確実にしたい場合に本当に役立ちます。
ペーテルTörök

1
@ペーター:私はこれについて言及するために答えを更新していました。ありがとう。
Grodriguez

12

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

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

したがって、InterruptedExceptionを伝播するか、またはそれをインテリジェントに(つまり、本来の目的を達成した場所で)食べて、割り込みフラグをリセットします。割り込みフラグは、InterruptedExceptionがスローされるとクリアされることに注意してください。Jdkライブラリの開発者が想定しているのは、例外をキャッチするとそれを処理することになるため、デフォルトではフラグはクリアされています。

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

割り込みがどのように機能するか記述した答えを、例とともに示します。Runnableのrunメソッドでwhileループから抜け出すためにInterruptedExceptionを使用しているコード例を見ることができます。


10

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

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

簡単に言えば、単にInterruptedExceptionをスローした場合、スレッドが終了するデフォルトのインテントと一致しています。スローリストにInterruptedExceptionを追加できない場合は、RuntimeExceptionでラップします。

より適切な「デフォルト」処理を促進するため、InterruptedExceptionはそれ自体がRuntimeExceptionでなければならないという非常に合理的な議論があります。これは、RuntimeExceptionがコードのエラーを表すべきであるというカテゴリーのルールにデザイナーが固執したからといって、RuntimeExceptionではありません。コードのエラーが原因でInterruptedExceptionが直接発生するわけではないため、発生しません。しかし実際には、コードにエラー(つまり、無限ループ、デッドロック)があるためにInterruptedExceptionが発生することが多く、Interruptはそのエラーを処理する他のスレッドのメソッドです。

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

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

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

3

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

  • InterruptExceptionの伝播

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

またはさらに:

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

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

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


0

何もしなくても大丈夫な場合もあります。おそらくあなたがデフォルトでやるべきことではないかもしれませんが、割り込みが発生するはずがない場合は、他に何をすべきかわかりません(おそらくエラーをログに記録しますが、プログラムの流れには影響しません)。

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

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


これは悪いアドバイスです。割り込みを無視するほど重要なタスクについて考えるのに非常に苦労しています。Oracleはおそらく怠惰で、Thread.sleep()の呼び出しに(もっと)混乱を追加したくありませんでした。スレッドが中断されている理由がわからない場合は、実行中の処理をすべて停止する必要があります(スレッドをできるだけ早く終了させるために、例外を返すか、例外を再スローします)。
Ajax

だから私は時々言った。また、それはまだ提案されていなかったし、私の意見ではいくつかのケースでそれは完全に素晴らしいので。構築しているソフトウェアの種類に依存すると思いますが、24時間年中無休(JVMのシャットダウンを除く)のワーカースレッドがある場合は、BlockingQueueからアイテムを取得するなどの割り込みをエラーとして扱います(つまり、ログ)「それは起こらないはずです」から、再試行してください。当然のことながら、プログラムの終了をチェックするために別のフラグがあります。一部のプログラムでは、JVMのシャットダウンは通常の操作では発生しないはずです。
BjarneBoström16年

または別の言い方をすると、私の意見では、割り込み例外のために24時間年中無休で実行する必要のあるアプリケーションよりも、追加のエラーログステートメント(およびエラーログでメールが送信されるなど)を危険にさらす方が良いです。通常の状態では、どうなるかわかりません。
BjarneBoström16年

うーん、まあ、あなたのユースケースは異なるかもしれませんが、理想的な世界では、中断されたからといって終了するだけではありません。あなたは仕事を待ち行列から取り除くのをやめ、そのスレッドを死なせます。スレッドのスケジューラーも中断され、シャットダウンするように指示された場合、JVMは終了しており、そのゲームオーバーです。特に、kill -9を送信するか、メールサーバーなどを停止しようとすると、強制終了するまで無視され、コードの他の部分が正常にシャットダウンされなくなります。ディスクまたはdbへの書き込み中に強制的にkillすると、素晴らしいことが確実に起こります。
Ajax
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.