JavaFX:ExecutorService.awaitTermination不通过绑定属性

时间:2016-06-25 04:24:03

标签: java javafx

我有多个任务在一个线程中运行。这很有效,相应的tablecell进度条会按预期更新。在我执行它们之前,我想要禁用UI上的所有按钮(这是我的enableControls方法)。当最后一个任务运行时,我重新启用按钮。

我尝试了以下操作,添加了关闭和awaitTermination但是除非完成所有操作,否则UI不会更新。

ExecutorService exec = Executors.newSingleThreadExecutor(r -> {
   Thread t = new Thread(r);
   t.setDaemon(true); 
   return t ;
});

enableControls(false);
for (Segment seg : selectedItems) {
    Task loadingTask = new Task<Void>() {
        @Override
        public Void call() {
            for (int i = 1; i <= 100; i++) {
                updateProgress(i, 100);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }
    };
    seg.progressBarProperty().bind(loadingTask.progressProperty());
    loadingTask.setOnSucceeded(e -> seg.progressBarProperty().unbind());
    loadingTask.setOnFailed(e -> {
        loadingTask.getException().printStackTrace();
        enableControls(true);
    });
    exec.submit(loadingTask);
}
exec.shutdown();
try {
    exec.awaitTermination(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
    e.printStackTrace();
}
System.out.println("test");
enableControls(true);

或者我想到确定当前任务是否是最后一个(循环中不再有seg)并重新启用setOnSucceeded中的UI控件。至少陈述并不优雅。

任何想法为什么UI(通过绑定值)没有更新 - 这是awaitTermination的限制吗?

1 个答案:

答案 0 :(得分:1)

awaitTermination是一个阻塞调用,你不应该在JavaFX应用程序线程上调用它。只需创建一个新任务来管理任务执行并等待其他任务的终止。您可以对新任务使用绑定,以在UI运行时禁用部分UI。

run

import javafx.application.*;
import javafx.concurrent.Task;
import javafx.event.Event;
import javafx.geometry.Insets;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class Waiter extends Application {

    private static final int N_TASKS = 3;
    private static final int TOTAL_TASK_EXECUTION_TIME_LIMIT_SECS = 20;

    private final VBox layout = new VBox(10);

    @Override
    public void start(Stage stage) throws Exception {
        Button run = new Button("Run");
        run.setOnAction(this::work);

        layout.getChildren().add(run);
        layout.setPadding(new Insets(10));
        layout.setPrefSize(100, 150);

        stage.setScene(new Scene(layout));
        stage.show();
    }

    private void work(Event event) {
        Node controlNode = (Node) event.getSource();

        ExecutorService exec = Executors.newSingleThreadExecutor(r -> {
            Thread t = new Thread(r, "load");
            t.setDaemon(true);
            return t;
        });

        ExecutorService monitor = Executors.newSingleThreadExecutor(r -> {
            Thread t = new Thread(r, "monitor");
            t.setDaemon(true);
            return t;
        });

        final List<LoadingTask> tasks = new ArrayList<>();
        for (int j = 0; j < N_TASKS; j++) {
            LoadingTask loadingTask = new LoadingTask();
            ProgressBar progressBar = new ProgressBar();
            progressBar.progressProperty().bind(loadingTask.progressProperty());
            layout.getChildren().add(progressBar);

            tasks.add(loadingTask);
        }

        Task<Void> taskRunner = new Task<Void>() {
            @Override
            protected Void call() throws Exception {
                tasks.forEach(exec::submit);
                exec.shutdown();
                boolean cleanShutdown = exec.awaitTermination(
                        TOTAL_TASK_EXECUTION_TIME_LIMIT_SECS,
                        TimeUnit.SECONDS
                );
                if (!cleanShutdown) {
                    exec.shutdownNow();
                }

                Platform.runLater(() ->
                        layout.getChildren()
                                .removeIf(
                                        node -> node instanceof ProgressBar
                                )
                );

                return null;
            }
        };
        controlNode.disableProperty().bind(taskRunner.runningProperty());
        monitor.submit(taskRunner);
        monitor.shutdown();
    }

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

    private static class LoadingTask extends Task<Void> {
        @Override
        public Void call() {
            for (int i = 1; i <= 100; i++) {
                if (isCancelled() || Thread.interrupted()) {
                    break;
                }
                updateProgress(i, 100);
                try {
                    Thread.sleep(20);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
            return null;
        }
    }
}
  

我需要禁用舞台上的所有按钮和文本字段

如果禁用父节点,它将递归禁用父节点的所有子节点。因此禁用场景中的所有内容,只需绑定场景根的disableProperty:

controlNode.getScene().getRoot()
    .disableProperty().bind(taskRunner.runningProperty());

在父级上禁用输入的另一种方法是使用具有所有者集的WINDOW_MODALAPPLICATION_MODAL对话框。例如,ControlsFX ProgressDialog