了解如何更新线程中的GUI工作原理

时间:2014-08-28 12:58:48

标签: java multithreading javafx progress-bar task

我可以解释一下如何更新GUI线程吗?对我来说这一切都很混乱。 例如,我想在循环中更新ProgressBar。我知道我需要把循环放到一个新的Task中。我将progressBar的progressProperty绑定到了Task的进度。

如果我用

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

结合调用函数中的Task的updateProgress()方法,它工作正常,progressBar更新。

问题一:为什么我通过直接在循环内设置进度来更新ProgressBar失败(progressProperty没有绑定)?如果我想在循环内部设置(非)可见,也会出现相同的情况。

问题2:将progressProperty绑定到Task.progressProperty。为什么我不能通过使用Plateform.runLater(任务)调用任务来更新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();
}

1 个答案:

答案 0 :(得分:2)

  

问题一:为什么我通过直接在循环内设置进度来更新ProgressBar失败(progressProperty没有绑定)?如果我想在循环内部设置(非)可见,也会出现相同的情况。

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

来做到这一点

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

  

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

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

  

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

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

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