2010-12-01 9 views
9

私は、一定回数何かを計算する必要のあるアプリケーションを持っています。この計算関数には@Async(Spring Frameworkからのアノテーション)というアノテーションがあり、これらの計算を4つのスレッドで実行することができます。問題は、これらの計算が約40000必要であり、すべての計算の開始から終了までの時間を知りたいので、計算関数を呼び出すfor-loopの前後の時間を確認します。しかし、すべての計算が待ち行列に入れられるので、forループはすぐに終了し、時間は1秒のようなものですが、計算が完了するまでに数時間かかります。私は最大キューサイズを約100に設定しようとしましたが(メモリ使用量を減らすのにも効果があります)、これは解決策ではありません。 forループの直後にすべてのスレッドが作業を終えるまで実行コードを一時停止する方法はありますか?@Async注釈を使用することはできますか?@Asyncは、他のスレッドが終了するまでスレッドが続行されないようにします。

これは同じ問題を示すいくつかのコードである:

クラスを実行する:

public class Foo { 
    public void executeBlaALotOfTimes() { 
     long before = System.currentTimeMillis(); 

     for (int i = 0; i<40000; i++) { 
      executeBla(); 
     } 

     long after = System.currentTimeMillis(); 

     System.out.println("Time it took for a lot of bla to execute: " + (after - before)/1000.0 + " seconds."); 
    } 
} 

および計算を行うクラス:

@Service 
public class Bar { 
    @Async 
    public void executeBla() { 
     System.out.println("Bla!"); 
    } 
} 

これは以下の出力をもたらします(Fooのコードが無限に高速で実行されると仮定します)。

 
Time it took for a lot of bla to execute: 0.0 seconds. 
Bla! 
Bla! 
Bla! 
Bla! 
. 
. 
. 
etc 
+0

Async' @この春 'ですか? – skaffman

+0

申し訳ありません、これはSpring @Async – FinalArt2005

+0

を使用しています。 '@ Async'メソッドとその呼び出し方を教えてください。 – skaffman

答えて

29

実行が完了するまで待つ必要がある場合は、Futureを戻り値として返すことができます。

@Async 
public Future<Void> executeBla() { 
    System.out.println("Bla!"); 
    return new AsyncResult<Void>(null); 
} 

返される実際の値がありませんので、これは、やや人工的ですが、それはまだ呼び出し元のコードは最後まですべての実行を待つことができます:

ここ
public void executeBlaALotOfTimes() { 
    long before = System.currentTimeMillis(); 

    Collection<Future<Void>> futures = new ArrayList<Future<Void>>(); 

    for (int i = 0; i<40000; i++) { 
     futures.add(executeBla()); 
    } 

    for (Future<Void> future : futures) { 
     future.get(); 
    } 

    long after = System.currentTimeMillis(); 

    System.out.println("Time it took for a lot of bla to execute: " + (after - before)/1000.0 + " seconds."); 
} 

、オフ最初のループ火災非同期タスクを実行し、先物をリストに格納します。その後、秒ループは先物を繰り返し、それぞれが終了するのを待ちます。

1

代わりにListenableFutureを返し、CountDownLatchを使用することもできます。

@Async 
public ListenableFuture<Void> executeBla() { 
    try { 
     System.out.println("Bla!"); 
     return AsyncResult.forValue(null); 
    } catch (Throwable t) { 
     return AsyncResult.forExecutionException(t); 
    } 
} 

このシナリオでは、明示的にそれぞれの将来のためにfuture.get()を呼び出さないようにすることができます。この目的のために作成されたCountDownLatchを減少させる成功と失敗のコールバックを追加してこれを達成します。

public void executeBlaALotOfTimes() { 
    long before = System.currentTimeMillis(); 

    int numExecutions = 40000; 
    CountDownLatch countDownLatch = new CountDownLatch(numExecutions); 

    for (int i = 0; i<numExecutions; i++) { 
     ListenableFuture<Void> future = executeBla(); 
     future.addCallback(
      aVoid -> countDownLatch.countDown(), 
      throwable -> countDownLatch.countDown() 
     ); 
    } 

    try { 
     countDownLatch.await(); 
    } catch (InterruptedException e) { 
     // Handle exception 
    } finally { 
     long after = System.currentTimeMillis(); 
     System.out.println("Time it took for a lot of bla to execute: " + (after - before)/1000.0 + " seconds."); 
    } 

}

関連する問題