[英]Wait for the first thread to finish before running other threads in parallel
從我的主要流程開始,我想並行完成一些任務。 假設我有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");
}
您可能想看一下新的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)
));
您也可以使用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){}
});
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.