【问题标题】:Understanding how updating GUI in thread works了解在线程中更新 GUI 的工作原理
【发布时间】:2014-08-28 12:58:48
【问题描述】:

我能解释一下更新 GUI 线程是如何工作的吗?这对我来说都是一团糟。 例如,我想在循环中更新 ProgressBar。我知道我需要将循环放入一个新任务中。我将 progressBar 的 progressProperty 绑定到了 Task 的进度。

如果我用

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

结合调用函数中Task的updateProgress()方法,效果很好,progressBar更新了。

问题一:为什么我无法通过直接在循环内设置进度来更新 ProgressBar(progressProperty 未绑定)?如果我想在循环内设置它(不)可见,也会发生同样的情况。

问题2:让progressProperty绑定到Task.progressProperty。为什么我不能通过使用 Plateform.runLater(task) 调用 Task 来更新 progressBar?它不会更新 GUI 线程。

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

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();
}

【问题讨论】:

标签: java multithreading javafx progress-bar task


【解决方案1】:

问题一:为什么我无法通过直接在循环内设置进度来更新 ProgressBar(progressProperty 未绑定)?如果我想在循环内设置它(不)可见,也会发生同样的情况。

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

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

问题2:让progressProperty绑定到Task.progressProperty。为什么我不能通过使用 Plateform.runLater(task) 调用 Task 来更新 progressBar?它不会更新 GUI 线程。

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

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

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

顺便说一句,Brian Goetz 在他的Java 并发实践一书的第 9.1 章中描述了为什么 GUI 是单线程的?

【讨论】:

  • Platform.runLater(myTask),或等效地,Platform.runLater(()-&gt; myTask.call()) 失败的原因与您根本不使用后台线程失败的原因完全相同。您正在 FX 应用程序线程上运行代码并阻塞该线程:在任务完成之前无法更新 UI。
  • 正如@James_D 悲伤的那样,任何阻塞操作,如Thread.sleep、文件读/写、网络套接字读/写、读/写数据库或密集计算都应该在 JavaFX 应用程序线程之外运行否则 UI 将被冻结。
  • 你所指的任务,“myTask”,对应我的“任务”对吧?因为我无法访问 task.call(), call() has protected access in Task where V is a type variable .. 是什么意思?我尝试用public Object call() 替换protected Object call(),但它不起作用。
  • 是的,对不起;你不能打电话给Platform.runLater(() -&gt; myTask.call());。我只是从提供的答案中复制。相同的论点适用于Platform.runLater(task);,因此无论如何您都不会使用此代码。你读过answer linked above吗?如果这不能回答您的问题,请编辑问题并解释它有何不同。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-02-20
  • 2012-12-11
  • 2011-02-18
  • 2018-02-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多