@Async阻止线程继续,直到其他线程完成

我有一个应用程序,其中需要计算一定数量的东西。 此计算函数具有注释@Async(来自Spring Framework),这使得可以在4个线程上运行这些计算。 问题是我需要大约40000个这样的计算,我想知道所有计算的开始和结束之间的时间,所以我看看在调用计算函数的for循环之前和之后的时间。 但是现在所有的计算都被放入队列中,因此for循环立即结束,时间就像1秒钟,而计算完成需要几个小时。 我已经尝试将最大队列大小设置为大约100(也很好地减少了内存使用量),但这也没有解决方案,因为我将错过它花费的总时间中的最后100次计算。 有没有办法在for循环之后暂停执行代码,直到所有线程完成其工作,但仍然能够使用@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中的代码执行速度非常快):

执行大量bla需要的时间:0.0秒。
布拉!
布拉!
布拉!
布拉!
 。
 。
 。
等等

如果您需要等待执行完成,那么您可以返回Future作为返回值,例如

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

这有点人为,因为没有返回实际值,但它仍然允许调用代码等待所有执行完成:

 public void executeBlaALotOfTimes() { long before = System.currentTimeMillis(); Collection> futures = new ArrayList>(); for (int i = 0; i<40000; i++) { futures.add(executeBla()); } for (Future 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."); } 

这里,第一个循环触发异步任务并将期货存储在列表中。 秒循环然后迭代期货,等待每一个完成。

另一种方法是返回一个ListenableFuture并使用CountDownLatch

 @Async public ListenableFuture 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 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."); } 

}