6.6.7. マルチスレッド実行制御ハンドラ

本ハンドラは、サブスレッドを作成し、ハンドラキュー上の後続ハンドラの処理を各サブスレッド上で並行実行する。 このハンドラでの処理結果は、各サブスレッドでの実行結果を集約したオブジェクト(MultiStatus)となる。

本ハンドラでは、以下の処理を行う。

処理の流れは以下のとおり。

../../../../_images/flow22.png

6.6.7.2. モジュール一覧

<dependency>
  <groupId>com.nablarch.framework</groupId>
  <artifactId>nablarch-fw-standalone</artifactId>
</dependency>

6.6.7.3. 制約

特に無し

6.6.7.4. スレッド数を指定する

本ハンドラはデフォルトでは、後続のサブスレッドを1つだけ起動し、ハンドラを実行する。

後続の処理(例えばバッチアクションの処理)を並列化することで、パフォーマンス向上が見込まれる場合には、設定値を変更することで後続のハンドラの処理を多重化出来る。

以下に例を示す。

<component class = "nablarch.fw.handler.MultiThreadExecutionHandler">
  <!-- 後続ハンドラを8多重で実行する -->
  <property name="concurrentNumber" value="8" />
</component>

重要

本ハンドラ以降の処理を複数スレッドで実行する場合、後続のハンドラやバッチアクションなどはスレッドセーフな実装となっている必要がある。 スレッドセーフとなっている保証のない処理を安易に複数スレッドで実行すると、予期せぬ例外が発生したり、データ不整合の原因となるので注意すること。

6.6.7.5. スレッド起動前後で任意の処理を実行したい

このハンドラは、サブスレッド起動前及び終了後にコールバック処理を行う。

コールバック処理は以下の3つのポイントで実行される。

  • サブスレッド起動前
  • サブスレッドで例外発生後の全スレッド終了後
  • 全サブスレッド終了後(サブスレッドで例外が発生した場合でも実行される)

コールバックされる処理は、このハンドラより後続に設定されたハンドラの中で、 ExecutionHandlerCallback を実装しているものとなる。 もし、複数のハンドラが ExecutionHandlerCallback を実装している場合は、より手前に設定されているハンドラから順次コールバック処理を実行する。

重要

複数のハンドラがコールバック処理を実装していた場合で、コールバック処理中にエラーや例外が発生した場合は、 残りのハンドラに対するコールバック処理は実行しないため注意すること。

重要

コールバック処理で行ったデータベース処理は、親スレッド側のハンドラキューに設定されたデータベース接続とトランザクションが使用される。 このため、これらの処理で行った更新処理は本ハンドラ終了後に、親スレッド側に設定された トランザクション制御ハンドラ で確定(コミット)される。

もし、コールバック処理内で行った処理を即確定する必要がある場合には、親スレッド側に設定されたデータベース接続ではなく、個別のトランザクションを使用して処理を行うこと。

詳細は、以下を参照。

以下にコールバック処理の実装例を示す。

public class SampleHandler implements Handler<Object, Result>, ExecutionHandlerCallback<Object, Result> {

  @Override
  public Result handle(Object input, ExecutionContext context) {
    // ハンドラの処理を実装する。
    return context.handleNext(input);
  }

  @Override
  public void preExecution(Object input, ExecutionContext context) {
    // サブスレッド起動前のコールバック処理を実装する
  }

  @Override
  public void errorInExecution(Throwable error, ExecutionContext context) {
    // サブスレッドでエラーが発生した場合のコールバック処理を実装する
  }

  @Override
  public void postExecution(Result result, ExecutionContext context) {
    // サブスレッド終了後のコールバック処理を実装する
    // サブスレッド側の処理が正常に終了したかどうかは、引数のResultから判定できる。
    if (result.isSuccess()) {
        // サブスレッドが正常終了
    } else {
        // サブスレッドが異常終了
    }
  }
}

6.6.7.6. データベース接続に関する設定について

親スレッド側の処理でデータベース接続が必要となる場合には、本ハンドラ以前に データベース接続管理ハンドラ の設定が必要になる。 サブスレッド側でデータベースに対するアクセスが必要な場合には、本ハンドラ以降のサブスレッドで実行されるハンドラ構成に データベース接続管理ハンドラ の設定が必要となる。 (親スレッド、サブスレッドともに、データベース接続とセットでトランザクションを制御するハンドラも必要となる)

このため、親スレッド及びサブスレッドの両方でデータベースアクセスを行うハンドラ構成の場合、最低でも2つのデータベース接続が使用される。 サブスレッドが複数となる場合には、スレッド数分のデータベース接続が必要となる。例えば、サブスレッド数が10の場合、合計11個のデータベース接続が必要となる。

6.6.7.7. サブスレッドでの例外発生時の振る舞い

サブスレッド内で予期せぬ例外が発生した場合は、バッチアプリケーションを異常終了させるために ThreadPoolExecutor#shutdownNow() を呼び出して、例外が発生していない他の処理中のサブスレッドを実行中のデータ処理完了後に安全に終了させる。

サブスレッド側に データベース接続管理ハンドラ 及び トランザクション制御ハンドラ を設定して サブスレッド毎にトランザクション管理する場合に、サブスレッドで例外が発生した場合の親スレッド及びサブスレッドの動作を以下に示す。

../../../../_images/exception_flow.png
  1. 例外が発生したサブスレッドは処理が中断されロールバックされる。
  2. 親スレッドは各サブスレッドで使用されるデータリーダをクローズする。
  3. 親スレッドは全てのサブスレッドに対し停止要求を行う。
  4. 各サブスレッドは 2. で既にデータリーダがクローズされているため、 実行中の処理が終わったタイミングで正常終了する。

重要

InterruptedException を捕捉している場合は、 割り込み要求により安全に処理できないことを示しているため、例外を送出する等で処理を異常終了させること。