2

本質的にマルチスレッドである 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 を使用する主な利点です。結果が返される順序は実際には気にしません。結果が順番に返されるのを待っている間、不必要にブロックすることは避けたいと思います。

4

4 に答える 4

2

を共有しますが、を共有しExecutorませんCompletionService

まさにこれを行い、すべての簿記を処理するAsyncCompleterがあり、次のことが可能になります。

Iterable<Callable<A>> jobs = jobs();
Iterable<A> results async.invokeAll(jobs);

results返された順に繰り返し、結果が得られるまでブロックします

于 2011-02-16T10:47:44.123 に答える
1

通常の共有 ExecutorService を使用できます。タスクを送信するたびに、送信したばかりのタスクの Future が返されます。それらすべてをリストに保存して、後でクエリを実行できます。

例:

private final ExecutorService service = ...//a single, shared instance

void handleRequest(Integer[] input) {
    // Submit tasks
    List<Future<Integer>> futures = new ArrayList<Future<Integer>>(input.length);
    for (final Integer i : input) {
        Future<Integer> future = service.submit(new Callable<Integer>() {
            public Integer call() {
                return -1 * i;
            }
        });
        futures.add(future);
    }

    // Do other stuff...

    // Get task results
    for(Future<Integer> f : futures){
        try {
            Integer result = f.get();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
于 2011-02-16T08:23:00.927 に答える
0

なぜ必要なのCompletionServiceですか?

各スレッドCallablesは、ExecutorService. その後、各スレッドは独自のプライベートFuture参照を保持します。

また、Executorその子孫は設計上スレッドセーフです。実際に必要なのは、各スレッドが独自のタスクを作成し、その結果を検査できることです。

Javadocjava.util.concurrentは優れています。使用パターンと例が含まれています。ExecutorServiceおよびその他のタイプのドキュメントを読んで、それらの使用方法をよりよく理解してください。

于 2011-02-16T08:22:04.277 に答える