398

次の取り扱い方法の違いは何InterruptedExceptionですか?それを行うための最良の方法は何ですか?

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

また

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

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

4

7 に答える 7

532

次の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;
    }
    
  • いいえの場合は、でメソッドを宣言しないでくださいthrows InterruptedException。例外をキャッチする必要があります(必須です!)。この状況で覚えておくべき重要なことが2つあります。

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

    2. スレッドが中断されたという事実の場合にメソッドが適切な戻り値を生成することができたとしても、それでも重要な場合があります。InterruptedException特に、メソッドを呼び出すコードは、メソッドの実行中に中断が発生したかどうかに関心がある場合があります。したがって、中断フラグを設定して、中断が発生したという事実をログに記録する必要があります。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)今では、ただやることは悪い考えであることは明らかです。発信者にはあまり礼儀正しくありません。新しいランタイム例外を発明することはできますが、根本的な原因(誰かがスレッドの実行を停止したい)が失われる可能性があります。

その他の例:

実装Runnable:ご存知かもしれませんが、の署名でRunnable.runは再スローできませんInterruptedExceptions。さて、あなたは実装Runnableにサインアップしました。つまり、可能性に対処するためにサインアップしたということですInterruptedExceptions。などの別のインターフェースを選択するか、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;
}

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

于 2010-10-20T09:24:52.117 に答える
116

たまたま、私は今朝、BrianGoetzによるJavaConcurrencyInPracticeで作業する途中でこのことについて読んでいまし。基本的に彼はあなたが3つのことのうちの1つをするべきだと言います

  1. 伝播InterruptedExceptionInterruptedException-発信者がそれに対処しなければならないように、チェックをスローするようにメソッドを宣言します。

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

  3. メソッド内の中断を無視しますが、終了時にステータスを復元します-たとえば、Guavaを介してUninterruptiblesUninterruptiblesJCIP§7.1.3のキャンセル不可のタスクの例のようにボイラープレートコードを引き継ぎます。
于 2010-10-20T09:45:23.877 に答える
24

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

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

を投げる他のオプションに関してはRuntimeException、それを行うのはあまり賢明なことではないようです(誰がこれをキャッチしますか?どのように処理されますか?)が、追加情報なしで詳細を伝えることは困難です。

于 2010-10-20T09:25:11.973 に答える
12

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

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

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

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

例を挙げて、割り込みがどのように機能するかを説明するために私が書いた回答を次に示します。サンプルコードでは、Runnableのrunメソッドのwhileループから抜け出すためにInterruptedExceptionを使用していることがわかります。

于 2011-08-03T07:10:04.290 に答える
12

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

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

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

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

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

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

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

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

  • InterruptExceptionの伝播

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

またはさらに:

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

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

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

于 2013-12-17T02:38:10.953 に答える
0

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

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

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

于 2015-02-25T13:58:38.497 に答える