@Async 阻止线程继续,直到其他线程完成
我有一个应用程序需要计算一定次数的东西.此计算函数具有注释 @Async(来自 Spring 框架),这使得可以在 4 个线程上运行这些计算.问题是我需要大约 40000 次这样的计算,我想知道所有计算的开始和结束之间的时间,所以我看到调用计算函数的 for 循环之前和之后的时间.但是现在所有的计算都放在一个队列中,所以 for 循环立即结束,时间大约是 1 秒,而计算需要几个小时才能完成.我尝试将最大队列大小设置为大约 100(也有助于减少内存使用),但这也不是解决方案,因为我会错过总时间的最后 100 次计算.有没有办法在 for 循环之后暂停执行代码,直到所有线程完成工作,但仍然能够使用 @Async 注释?
这是一些说明相同问题的代码:
执行类:
公共类 Foo {公共无效执行BlaALotOfTimes(){很久以前 = System.currentTimeMillis();for (int i = 0; i<40000; i++) {执行布拉();}很久之后 = System.currentTimeMillis();System.out.println("大量bla执行的时间:" + (after - before)/1000.0 + " seconds.");}}
以及执行计算的类:
@Service公共类酒吧{@异步公共无效执行Bla(){System.out.println("Bla!");}}
这将导致以下输出(假设 Foo 中的代码执行得无限快):
<上一页>执行大量 bla 所花费的时间:0.0 秒.布拉!布拉!布拉!布拉!...等等 解决方案如果你需要等待执行完成,那么你可以返回一个 Future
作为返回值,例如
@Async公共未来<无效>执行布拉(){System.out.println("Bla!");return new AsyncResult<Void>(null);}
这有点人为,因为没有返回实际值,但它仍然允许调用代码等待所有执行完成:
public void executeBlaALotOfTimes() {很久以前 = System.currentTimeMillis();收藏<未来<虚空>>futures = new ArrayList>();for (int i = 0; i<40000; i++) {期货.add(executeBla());}for (Future<Void> 未来:期货) {未来.get();}很久之后 = System.currentTimeMillis();System.out.println("大量bla执行的时间:" + (after - before)/1000.0 + " seconds.");}
在这里,第一个循环触发异步任务并将未来存储在列表中.秒循环然后迭代未来,等待每个完成.
I have an application where for a certain number of times something needs to be calculated. This calculation function has the annotation @Async (from the Spring Framework), that makes it possible to run these calculations on 4 threads. The problem is that I need about 40000 of these calculations and I want to know the time between the start and end of all the calculations, so I see what time it is before and after the for-loop that calls the calculation functions. But now all the calculations are put in a queue, so the for loop finishes immediately and the time is something like 1 second, while it takes a couple of hours for the calculations to complete. I've tried setting a max queue size to about 100 (also good to reduce memory usage) but this is also no solution since I'll miss the last 100 calculations in the total time it takes. Is there a way to pause the executing code just after the for loop until all threads have finished doing their work, but still being able to use the @Async annotation?
This is some code that illustrates the same problem:
Executing class:
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.");
}
}
And the class that performs the calculations:
@Service
public class Bar {
@Async
public void executeBla() {
System.out.println("Bla!");
}
}
This would result in the following output (assuming the code in Foo executes infinitely fast):
Time it took for a lot of bla to execute: 0.0 seconds. Bla! Bla! Bla! Bla! . . . etc
解决方案
If you need to wait for the executions to finish, then you can return a Future
as a return value, e.g.
@Async
public Future<Void> executeBla() {
System.out.println("Bla!");
return new AsyncResult<Void>(null);
}
This is slightly artificial, since there's no actual value being returned, but it will still allow the calling code to wait for all executions to finish:
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.");
}
Here, the first loop fires off the async tasks and stores the futures in a list. The seconds loop then iterates over the futures, waiting for each one to finish.
相关文章