web-dev-qa-db-ja.com

Javaスレッドでの例外処理のベストプラクティス

長年の読者、初めての質問者はこちら。データベースにデータをバッチで書き込むサービスがあります。別のスレッドによって監視されているバッファーが含まれています。バッファが最後の書き込みから特定のサイズに達するか、一定の時間が経過すると、スレッドは常にデータをDBに書き込みます。

Javaでは、監視スレッドに例外が発生すると、通知なしで終了し、何も実行されません。

そのようなスレッドで例外を処理する確立されたベストプラクティスはありますか?スレッドは常に実行されている必要があり、例外が発生した場合はログに例外を出力する必要があります。

このことを頭に浮かぶ可能性のある解決策がいくつかあります。

  1. メソッド全体をtry/catchブロックでラップし、すべての例外をキャッチして、ロガーにメッセージを出力させます。 (私の味にはあまりにも醜いです)
  2. キャッチされなかった例外ハンドラを登録し、そこで例外を処理します。 (多分?)
  3. スレッドを使用せず、代わりにCallableまたはRunnableを使用してください。 (これが常に実行中のスレッドに適しているかどうかはわかりません)
  4. 他に何か?
7
JohnEye

最初に、私はあなたが何のために監視スレッドを必要とし、それが何の目的であるかを理解していませんでした。私が考えることができる唯一の理由は、モニタースレッドが、バッファーを監視し、定期的にデータベースの書き込みを呼び出すだけの継続的に実行されるスレッドである場合です。それが目的である場合、私はむしろスケジューラでそれを行います。 n秒ごとに書き込みをスケジュールし、バッファ全体をデータベースに書き込みます。次に、バッファがオーバーフローしないようにするために、スケジューラレートが必要な秒数を概算する必要があります。

スレッドでの例外処理については、まず書き込みを再試行します。最終的に、すべてが失敗した場合、最初にすべきことは、データが失われないようにサルベージすることです。

例外を処理する方法は、発生する可能性のある例外を特定し、それらの特定の例外のみを処理することです。 catch (Exception e)のようなことをしてはいけません。 DBライターサービスは一般的なサービスであるため、例外を呼び出し元に伝達し、呼び出し元に例外を処理させます。これは、サービスが呼び出し元の希望する動作を認識できないためです。

これらの特定の例外をどのように処理するかについてはあなた次第であり、だれもそれを手助けすることはできません。 finallyブロックで例外を処理できず、例外が開発者の注意を必要とする場合は、例外をログに記録するか、代替の通信チャネル(例:メール、Slack。

6
leonz

スローされた例外をメインスレッドで処理/ログに記録する必要があるかどうかについては言及していません。 「それは問題ではありません。例外をログに記録し、スレッドの破損を回避するだけでよい」と想定します。

私の仮定が正しければ、Runnableのrunメソッドにtry/catchを置くだけでよいかもしれませんが、そのようなメソッドは無限ループで実行され、時々スリープする必要があります。

final Thread t = new Thread(new Runnable(){
    @Override
    public void run(){
          long lastTimeFlushed = getCurrentTime();
          while(true){
                try{
                     if(bufferLimitExceeded() || timeoutExceeded(lastTimeFlushed)){
                           flushToDB();
                           lastTimeFlushed = getCurrentTime();
                     } 
                     Thread.sleep(500);
                }catch(final InterruptedException ex){
                     // you'll need to decide whether to ignore or not thread's interruption
                     // This example is in case you decide not to ignore it
                     Thread.currentThread().interrupt();
                }catch(final Exception ex){
                     logError(ex);
                } 
          } 
     } 
});

t.start();

このようにして、スレッドが死ぬことはなく、必要なのは1つの場所にtry/catchを追加するだけです。

免責事項:私は自分の電話を使用してこれを書いた。タイプミスがある場合は申し訳ありません(特にコード内)

4
DanielCuadra