java - 了解线程中更新 GUI 的工作原理

标签 java multithreading javafx progress-bar task

我可以解释一下更新 GUI 线程是如何工作的吗?对我来说这一切都很困惑。 例如,我想循环更新 ProgressBar。我知道我需要将循环放入新任务中。我将 ProgressBar 的 ProgressProperty 绑定(bind)到任务的进度。

如果我用

调用任务
new Thread(task).start();

结合调用函数中Task的updateProgress()方法,运行正常,进度条更新了。

问题一:为什么我在循环内直接设置进度(progressProperty 未绑定(bind))来更新 ProgressBar 时失败?如果我想将其设置在循环内部(不)可见,也会发生同样的情况。

问题2:让progressProperty绑定(bind)到Task.progressProperty。为什么我无法通过使用 Plateform.runLater(task) 调用任务来更新进度条?它不会更新 GUI 线程。

问题 3:如何设置循环内进度条的可见性?

public class PRogressBar extends Application {

    ProgressBar progressBar;

    @Override
    public void start(Stage stage) {

        /**
            Task for updating the ProgressBar
        */
            Task<Void> task = new Task() {

                @Override
                protected Object call() throws Exception {
                    System.out.println("Init Task");
                    // progressBar.setVisible(false) // Question 3
                    // progressBar.setProgress(0.75) // question 1
                    for (int i = 1; i < 5; i++) {
                        final int update = i;
                        try {
                            Thread.sleep(500);
                            System.out.println("Run later : " + update/5.0);

                            updateProgress(i, 5);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    return null;
                }
            };

        /*
            Initializing the GUI
        */
        progressBar = new ProgressBar();
        Button button = new Button("blou");
        button.setOnAction((event) -> {
//            Platform.runLater(task); // Question 2

            Thread th = new Thread(task);
//            th.setDaemon(true);
             th.start();

             System.out.println("Thread started");



        });

        StackPane layout = new StackPane();
        layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10;");
        HBox hbox = new HBox(progressBar, button);
        layout.getChildren().add(hbox);
        progressBar.setProgress(0.1);
//        setVisible2(false);

//        progressBar.progressProperty().bind(task.progressProperty());

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

最佳答案

Question one : why do I fail at updating the ProgressBar by setting directly its progress inside of the loop (progressProperty being not binded) ? Same occurs if I want to set it (non-)visible inside of the loop.

因为影响当前显示舞台的所有 UI 更改都必须在 JavaFX 应用程序线程中执行。您的任务在您自己的线程中执行,因此请确保在 JavaFX 应用程序线程中调用 setProgresssetVisible。您必须通过 Platform.runLater(() -> {//change UI});

来实现此操作

updateProgressupdateMessage线程安全。也许这些方法会影响 UI,例如。 G。如果您已将 ProgressProperty 绑定(bind)到 ProgressBar。您可以以安全的方式从工作线程调用它。但updateProgress是异常(exception)而不是规则。

Question 2 : Let the progressProperty be binded to the Task.progressProperty. Why can't I update the progressBar by calling the Task with Plateform.runLater(task) ? It won't update the GUI thread.

你的意思是Platform.runLater(() -> myTask.call());?这应该可行,因为 call 在 JavaFX 应用程序线程中执行,因此您可以更改 UI。但这不是您处理任务的方式:)

Question 3 : how do I set the visibility of the progressBar inside of the loop?

您位于 JavaFX 应用程序线程之外,因此必须使用 Platform.runLater(...)

顺便说一句,Brian Goetz 在他的《Java Concurrency in Practice》一书中第 9.1 章描述了为什么 GUI 是单线程的?

关于java - 了解线程中更新 GUI 的工作原理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25549423/

相关文章:

java - java中出现以下问题如何解决?

c - pthread_join() 混合线程输出

python - 使用线程在后台加载内容

java - 使用 Factory 专门创建对象

java - 线性回归中的梯度下降

c++ - Boost Thread - 创建一个没有 join() 的线程

java - 关于javafx?

JavaFX Windows 10 native bundle 部署 - 异常访问冲突

java - 有没有一种方法可以更快地编译/加载 fxml 文件并且仅一次,而不是在每次重新启动应用程序时?

java - 覆盖实例化 Java 对象中的方法