37

まず第一に、私は API java.util.concurrent にまったく慣れていないので、おそらく私がやっていることは完全に間違っていると言わなければなりません。

私は何をしたいですか?

基本的に 2 つの別個の処理 ( myFirstProcessmySecondProcessと呼ばれる) を実行する Java アプリケーションがありますが、これらの処理は同時に実行する必要があります。

だから、私はそれをやろうとしました:

public void startMyApplication() {
    ExecutorService executor = Executors.newFixedThreadPool(2);
    FutureTask<Object> futureOne = new FutureTask<Object>(myFirstProcess);
    FutureTask<Object> futureTwo = new FutureTask<Object>(mySecondProcess);
    executor.execute(futureOne);
    executor.execute(futureTwo);
    while (!(futureOne.isDone() && futureTwo.isDone())) {
        try {
            // I wait until both processes are finished.
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    logger.info("Processing finished");
    executor.shutdown();
    // Do some processing on results
    ...
}

myFirstProcessmySecondProcessは を実装するクラスでCallable<Object>あり、すべての処理は call() メソッドで行われます。

それは非常にうまく機能していますが、それが正しい方法であるかどうかはわかりません。私がやりたいことをする良い方法はありますか?そうでない場合は、コードを強化するためのヒントを教えてください (できるだけシンプルに保ちます)。

4

6 に答える 6

45

メソッドを使ったほうがいいですget()

futureOne.get();
futureTwo.get();

どちらも処理が終了したというスレッドからの通知を待ちます。これにより、効率的でも洗練されていない、現在使用しているタイマー付きのビジー待機を節約できます。

おまけとしてget(long timeout, TimeUnit unit)、スレッドがスリープして応答を待機し、それ以外の場合は実行を継続する最大時間を定義できる API があります。

詳細については、Java APIを参照してください。

于 2009-02-11T11:16:03.903 に答える
18

上記の使用FutureTaskは許容範囲内ですが、間違いなく慣用的ではありません。実際には、に送信したものを余分 にラップしています。あなたはによってとして扱われます。内部的には、-as-を newでラップし、 として返します。FutureTaskExecutorServiceFutureTaskRunnableExecutorServiceFutureTaskRunnableFutureTaskFuture<?>

代わりに、Callable<Object>インスタンスをCompletionService. Callableviaに2 つの をドロップしsubmit(Callable<V>)、向きを変えて 2 回呼び出しますCompletionService#take()(送信された ごとに 1 回Callable)。これらの呼び出しは、サブミットされたタスクが 1 つ完了するまでブロックされ、次に他のサブミットされたタスクが完了するまでブロックされます。

既に がExecutor手元にある場合は、その周りに新しい を作成しExecutorCompletionService、そこにタスクをドロップします。回転して待って寝ないでください。CompletionService#take()タスクのいずれかが完了する (実行が終了するかキャンセルされる) か、待機中のスレッドtake()が中断されるまでブロックされます。

于 2009-11-22T23:30:55.780 に答える
17

Yuvalのソリューションは問題ありません。別の方法として、これを行うこともできます。

ExecutorService executor = Executors.newFixedThreadPool();
FutureTask<Object> futureOne = new FutureTask<Object>(myFirstProcess);
FutureTask<Object> futureTwo = new FutureTask<Object>(mySecondProcess);
executor.execute(futureOne);
executor.execute(futureTwo);
executor.shutdown();
try {
  executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
  // interrupted
}

このアプローチの利点は何ですか? この方法でエグゼキューターがそれ以上のタスクを受け入れるのを停止することを除いて、実際には大きな違いはありません (他の方法でも同じことができます)。でも、私はあのイディオムよりもこのイディオムを好む傾向があります。

また、いずれかの get() が例外をスローすると、両方のタスクが完了したと想定するコードの一部になる可能性があり、これは悪いことかもしれません。

于 2009-02-11T11:26:50.213 に答える
5

スレッドを同時に開始する場合、またはスレッドが終了するのを待ってからさらに処理を行う場合は、 CyclicBarrierを使用することをお勧めします。詳細については、javadoc を参照してください。

于 2009-02-11T11:22:39.893 に答える
2

futureTasks が 2 つ以上の場合は、 を検討してください[ListenableFuture][1]

別の操作が開始されるとすぐにいくつかの操作を開始する必要がある場合 (「ファンアウト」)、ListenableFuture は機能します。要求されたすべてのコールバックをトリガーします。もう少し作業を進めると、他のいくつかの先物がすべて終了するとすぐに、 「ファンイン」または ListenableFuture をトリガーして計算することができます。

于 2013-12-12T03:02:42.277 に答える