簡體   English   中英

如何等待多個服務完成?

[英]How to wait for multiple services to complete?

我像這樣運行多個服務:(例如在多個線程中讀取文件)

for (int i = 0; i < 3; i++) {
        ReadService readService = new ReadService();
        readService.start();
}

//wait until all services have been completed
System.out.println("All services done!");

ReadService是一個 class ,它擴展了Service class 並且正在做一些事情,比如讀取文件。 它是從另一個不是 JavaFX 應用程序線程的線程調用的。

我怎樣才能等到所有這些服務都完成才能調用System.out.println

可重現的例子:

import javafx.application.Application;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.stage.Stage;

public class HelloApplication extends Application {

    @Override
    public void start(Stage stage) {
        for (int i = 0; i < 3; i++) {
            ReadService readService = new ReadService();
            readService.start();
        }
        // wait until all services have been completed
        System.out.println("Services done");
        // continue with some other code, for example check the read file on anything
    }

    public static void main(String[] args) {
        launch();
    }
}

class ReadService extends Service<Boolean> {

    @Override
    protected Task<Boolean> createTask() {
        return new Task<>() {
            @Override
            protected synchronized Boolean call() throws Exception {
                // do something, for example read a file
                System.out.println("wait...");
                wait(5000);
                System.out.println("waiting done");
                return null;
            }
        };
    }
}

您正在從 FX 應用程序線程(執行start()方法的位置)啟動服務,並且不能阻塞該線程。 所以一種方法是為完成的服務數量創建一個計數器,並在它達到零時做出響應。

請注意,此處的所有新內容(創建和更新servicesPending屬性,以及在服務完成時執行的代碼)都在 FX 應用程序線程上執行,因此如果您在服務完成時更新 UI,這種方法是合適的。

@Override
public void start(Stage stage) {

    int numServices = 3 ;

    IntegerProperty servicesPending = new SimpleIntegerProperty(numServices);

    servicesPending.addListener((obs, oldValue, newValue) -> {
         if (newValue == 0) {
             // code to execute when all services are complete
             System.out.println("Services done");
         }
    });

    for (int i = 0; i < numServices; i++) {
        ReadService readService = new ReadService();
        readService.setOnSucceeded(e -> servicesPending.set(servicesPending.get() - 1));
        readService.start();
    }
    
}

另一方面,如果您在服務完成時所做的工作與 UI 無關,那么您可以創建一個新線程,該線程在服務完成之前一直阻塞,然后在該后台線程上執行工作。 實現此目的的一種方法是使用CountDownLatch

@Override
public void start(Stage stage) {

    int numServices = 3 ;

    CountDownLatch latch = new CountDownLatch(numServices);

    for (int i = 0; i < numServices; i++) {
        ReadService readService = new ReadService();
        readService.setOnSucceeded(e -> latch.countDown());
        readService.start();
    }

    Thread onServicesCompleted = new Thread(() -> {
        try {
            latch.await();
        } catch(InterruptedException exc) {
            Thread.currentThread().interrupt();
        }
        System.out.println("Services done");
        // other work to do when services are complete...
    });
    onServicesCompleted.start();
    
}

@jewelsea 在評論中提出了類似的解決方案。 如果你使用Task而不是Service ,你可以調用get() ,它會阻塞直到任務完成:

@Override
public void start(Stage stage) {

    int numServices = 3 ;

    List<Task<Boolean>> tasks = new ArrayList<>();
    ExecutorService exec = Executors.newCachedThreadPool();


    for (int i = 0; i < numServices; i++) {
        ReadService readService = new ReadService();
        tasks.add(readService);
    }

    exec.invokeAll(tasks);

    Task<Void> onServicesCompleted = new Task<>() {
        @Override
        protected Void call() throws Exception {
            for (Task<Boolean> task : tasks) {
                task.get();
            }
            System.out.println("Services Done");
            // other work to be done...
        }
    };
    exec.execute(onServicesCompleted);

}

class ReadService extends Task<Boolean> {
    @Override
    protected synchronized Boolean call() throws Exception {
        // do something, for example read a file
        System.out.println("wait...");
        wait(5000);
        System.out.println("waiting done");
        return null;
    }
}

如果您想在各個ReadService都完成后做更多的后台工作,然后想在此之后做 UI 工作(只需使用onServicesCompleted.setOnSucceeded(...) ),這個解決方案就很好。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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