从我的主要流程中我想并行完成一些任务。假设我有4个任务要在单独的流程中执行,但在这4个任务中,第一个任务需要在我可以并行运行其他任务之前完成。
我认为我的代码按照我的要求运行,但是有更好的方法吗?
public static void main(String[] args) {
System.out.println("In main");
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
runParalleltasks();
});
executor.shutdown();
System.out.println("Exiting main");
}
private static void runParalleltasks() {
System.out.println("Running parallel tasks");
doTask1();
ExecutorService executor = Executors.newFixedThreadPool(3);
executor.submit(() -> {
doTask2();
});
executor.submit(() -> {
doTask3();
});
executor.submit(() -> {
doTask4();
});
executor.shutdown();
System.out.println("Exiting parallel tasks");
}
答案 0 :(得分:3)
您可能需要查看新的CompletableFuture
工具。它可能不会像你这样的小案例提供很多帮助,但它提供了很大的灵活性:
import static java.util.concurrent.CompletableFuture.allOf;
import static java.util.concurrent.CompletableFuture.runAsync;
runAsync(() -> doTask1(), executor)
.thenCompose(v -> allOf(
runAsync(() -> doTask2(), executor),
runAsync(() -> doTask3(), executor),
runAsync(() -> doTask4(), executor)
));
如果你需要将task1的输出传递给依赖任务,那么这种方法真的会大放异彩。假设doTask1
返回String
而doTask2
接受String
。现在我们应该使用runAsync
来代替supplyAsync
而不是supplyAsync(() -> doTask1(), executor)
.thenCompose(resultOf1 -> allOf(
runAsync(() -> doTask2(resultOf1), executor),
runAsync(() -> doTask3(), executor),
runAsync(() -> doTask4(), executor)
));
:
{
"aps": {
"badge": 10,
"alert": "Hello world!",
"sound": "cat.caf"
}
}
答案 1 :(得分:2)
您也可以使用ExecutorService
执行此操作,这与您的原始方法非常相似。您所要做的就是使用 submit
的结果,这允许后续任务等待其完成:
ExecutorService executor = Executors.newFixedThreadPool(4);
Future<?> task1 = executor.submit(() -> doTask1());
Stream.<Runnable>of(() -> doTask2(), () -> doTask3(), () -> doTask4())
.forEach(r -> executor.submit(() -> { try {
task1.get();
r.run();
} catch(InterruptedException|ExecutionException ex){}
}));
executor.shutdown();
我们必须在等待第一个任务完成时捕获异常,但如果第一个任务失败或被取消,这也有可能故意跳过下一个任务。由于通常情况下,任务之间的依赖关系存在,因为后续任务需要第一个任务的结果,通过get()
等待是自然的方式。只要doTask1()
返回一个值,上面的submit
调用就会使用submit(Callable)
而不是submit(Runnable)
,而返回的Future
会有一个反映回报的泛型类型方法的类型,因此可以通过get()
方法检索结果。
为了避免死锁,必须满足以下条件中的至少一个:
在示例中,即使两者都适用。
请注意,上面的流使用不是必需的,经典循环也可以这样做:
for(Runnable r: Arrays.<Runnable>asList(() -> doTask2(), () -> doTask3(), () -> doTask4()))
executor.submit(() -> { try {
task1.get();
r.run();
} catch(InterruptedException|ExecutionException ex){}
});