本質的にマルチスレッドである Tomcat コンテナー内で実行されている単純な Web サービスがあります。サービスに送られる各リクエストで、外部サービスへの同時呼び出しを行いたいと考えています。java.util.concurrent の ExecutorCompletionService は、私を部分的にそこに連れて行きます。スレッド プールを提供すると、同時呼び出しの実行が処理され、結果の準備ができたときに通知されます。
特定の受信リクエストを処理するコードは次のようになります。
void handleRequest(Integer[] input) {
// Submit tasks
CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(Executors.newCachedThreadPool());
for (final Integer i : input) {
completionService.submit(new Callable<Integer>() {
public Integer call() {
return -1 * i;
}
});
}
// Do other stuff...
// Get task results
try {
for (int i = 0; i < input.size; i++) {
Future<Integer> future = completionService.take();
Integer result = future.get();
// Do something with the result...
}
} catch (Exception e) {
// Handle exception
}
}
これはうまく機能するはずですが、着信要求ごとに新しいスレッド プールが割り当てられるため、非常に非効率的です。CompletionService を共有インスタンスとして移動すると、同じ CompletionService とスレッド プールを共有する複数のリクエストでスレッド セーフの問題が発生します。リクエストがタスクを送信して結果を取得すると、取得する結果は送信したものとは異なります。
したがって、私が必要としているのは、スレッドセーフな CompletionService です。これにより、すべての着信要求で共通のスレッド プールを共有できます。各スレッドがタスクを完了すると、着信要求に適したスレッドに通知して、結果を収集できるようにする必要があります。
この種の機能を実装する最も簡単な方法は何ですか? このパターンは何度も適用されてきたと思います。これが Java 同時実行ライブラリによって提供されるものなのか、Java 同時実行ビルディング ブロックの一部を使用して簡単に構築できるのかはわかりません。
更新:忘れていた 1 つの警告は、提出したタスクが完了したらすぐに通知を受け取りたいということです。これは、タスクと結果の生成と消費を分離するため、CompletionService を使用する主な利点です。結果が返される順序は実際には気にしません。結果が順番に返されるのを待っている間、不必要にブロックすることは避けたいと思います。