簡體   English   中英

等待第一個線程完成后再並行運行其他線程

[英]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返回一個StringdoTask2接受一個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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM