36

複数のタスクを非同期で実行する必要があるJavaプロジェクトに取り組んでいます。私はExecutorがこれを行うための最良の方法であると信じるように導かれているので、私はそれに精通しています。(学ぶために報酬を得ることができます!)しかし、私がやろうとしていることを達成するための最善の方法が何であるかは私にはわかりません。

議論のために、2つのタスクを実行しているとしましょう。どちらも終了することは期待されておらず、アプリケーションの存続期間中は両方を実行する必要があります。次のようなメインラッパークラスを作成しようとしています。

  • いずれかのタスクが例外をスローした場合、ラッパーはそれをキャッチしてタスクを再開します。
  • いずれかのタスクが完了するまで実行されると、ラッパーはタスクに気づき、再起動します。

ここで、両方のタスクの実装はrun()、ループを中断することなくすべての実行時例外を処理する必要があるtry / catchブロックを使用して、完全に実行されることのない無限ループにコードをラップすることに注意してください。私は確実性の別の層を追加しようとしています。私または私をフォローしている誰かが、これらのセーフガードを無効にしてタスクを停止するような愚かなことをした場合、アプリケーションは適切に反応する必要があります。

私よりも経験豊富な人々が推奨するこの問題に取り組むためのベストプラクティスはありますか?

FWIW、私はこのテストクラスを作成しました:


public class ExecTest {

   private static ExecutorService executor = null;
   private static Future results1 = null;
   private static Future results2 = null;

   public static void main(String[] args) {
      executor = Executors.newFixedThreadPool(2);
      while(true) {
         try {
            checkTasks();
            Thread.sleep(1000);
         }
         catch (Exception e) {
            System.err.println("Caught exception: " + e.getMessage());
         }
      }
   }

   private static void checkTasks() throws Exception{
      if (results1 == null || results1.isDone() || results1.isCancelled()) {
         results1 = executor.submit(new Test1());
      }

      if (results2 == null || results2.isDone() || results2.isCancelled()) {
         results2 = executor.submit(new Test2());
      }
   }
}

class Test1 implements Runnable {
   public void run() {
      while(true) {
         System.out.println("I'm test class 1");
         try {Thread.sleep(1000);} catch (Exception e) {}
      }

   }
}

class Test2 implements Runnable {
   public void run() {
      while(true) {
         System.out.println("I'm test class 2");
         try {Thread.sleep(1000);} catch (Exception e) {}
      }
   }
}

それは私が望むように振る舞っていますが、私を驚かせるのを待っている落とし穴、非効率性、またはまったく間違った方向性があるかどうかはわかりません。(実際、私はこれに慣れていないので、何か間違っている/望ましくないことがなければ、私はショックを受けるでしょう。)

どんな洞察も歓迎します。

4

4 に答える 4

31

以前のプロジェクトでも同様の状況に直面し、怒っている顧客に直面してコードが爆発した後、仲間と私は2つの大きな安全策を追加しました。

  1. 無限ループでは、例外だけでなくエラーもキャッチします。例外のないことが起こり、Javaが例外ではなくエラーをスローすることがあります。
  2. バックオフスイッチを使用して、問題が発生して回復不能になった場合に、別のループを熱心に開始して状況を悪化させないようにします。代わりに、状況が通常に戻るまで待ってから、再開する必要があります。

たとえば、データベースがダウンし、ループ中にSQLExceptionがスローされたという状況がありました。残念な結果として、コードは再びループを通過し、同じ例外が再び発生するだけでした。ログは、同じSQLExceptionが1秒間に約300回発生したことを示しています。...これは断続的に数回発生し、5秒程度のJVMの一時停止が発生しました。その間、アプリケーションは応答しませんでしたが、最終的にエラーがスローされてスレッドが停止しました。

そのため、以下のコードにほぼ示されているバックオフ戦略を実装しました。例外が回復できない場合(または、数分以内に回復することを除いた場合)、操作を再開する前に、より長い時間待機します。

class Test1 implements Runnable {
  public void run() {
    boolean backoff = false;
    while(true) {
      if (backoff) {
        Thread.sleep (TIME_FOR_LONGER_BREAK);
        backoff = false;
      }
      System.out.println("I'm test class 1");
      try {
        // do important stuff here, use database and other critical resources
      }
      catch (SqlException se) {
       // code to delay the next loop
       backoff = true;
      }
      catch (Exception e) {
      }
      catch (Throwable t) {
      }
    }
  }
}

この方法でタスクを実装する場合、checkTasks()メソッドを使用して3番目の「ウォッチドッグ」スレッドを作成しても意味がありません。さらに、上記で概説したのと同じ理由で、エグゼキュータでタスクを再開することに注意します。まず、タスクが失敗した理由と、タスクを再実行すると便利な環境が安定しているかどうかを理解する必要があります。

于 2010-01-20T21:35:55.470 に答える
7

目を見張る以外に、私は通常、PMDFindBugsなどの静的分析ツールに対してJavaコードを実行して、より深い問題を探します。

特にこのコードの場合、FindBugsは、results1とresults2が遅延初期化で揮発性ではなく、run()メソッドが明示的に処理されていないために例外を無視する可能性があることを嫌っていました。

一般に、私は同時実行テストにThread.sleepを使用したり、タイマーを優先したり、状態/条件を終了したりすることに少し不安を感じています。Callableは、結果を計算できない場合に例外をスローする中断が発生した場合に何かを返すのに役立つ場合があります。

いくつかのベストプラクティスとより多くの思考の糧については、ConcurrencyinPracticeをチェックしてください。

于 2010-01-20T21:20:58.500 に答える
1

これはどう

Runnable task = () -> {
  try{
    // do the task steps here
  } catch (Exception e){
    Thread.sleep (TIME_FOR_LONGER_BREAK);
  }    
};
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleAtFixedRate(task,0, 0,TimeUnit.SECONDS);
于 2017-02-06T18:02:49.203 に答える
0

Quartzフレームワークを試しましたか?

于 2010-01-21T13:16:19.930 に答える