Platform.runLater() 如何发挥作用?

Kav*_*ora 2 java multithreading javafx runnable

我有一个简单的应用程序,它在后台更新数据,当它更新时,它会禁用所有其他按钮并启用 TextArea 来显示进度。

脚步:

  1. 禁用 mainUI 中的所有其他按钮(按钮名称:plotButton)

  2. 启用显示更新已开始的 TextArea(TextArea 名称:infoLogTextArea)

  3. 然后只启动更新方法(update() 抛出异常)。

这是代码:

@FXML
    public void handleUpdateButton() {
        
        infoLogTextArea.setVisible(true);
        infoLogTextArea.appendText("Please wait while downloading data from internet.....\n");      
        plotButton.setDisable(true);
        updateButton.setDisable(true);
            
        if(c!=null) {
            Runnable task = new Runnable() {
                @Override
                public void run() {
                    // Thread.sleep(10000); -> sleep for 10secs
                    Platform.runLater(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                c.updateData();
                                infoLogTextArea.appendText(c.getErrorLog().toString());
                                plotLabel.setText(c.getCityData().size()+" cities found and updated from internet");
                                infoLogTextArea.appendText("Successfully updated the data from Internet\n");
                            }catch (IOException e) {
                                infoLogTextArea.setText("Couldnot update the data from web: "+e.getMessage()+"\n");
                            }
                            finally {
                                plotButton.setDisable(false);
                                updateButton.setDisable(false);
                            }
                        }
                    });
                }
            };
            
            new Thread(task).start();
            
        }else {
            System.out.println("c not initialized");
        }
    }
Run Code Online (Sandbox Code Playgroud)

现在代码运行良好,但有时第 1 步和第 2 步没有执行,它开始第 3 步(更新),这会冻结程序。如果我将 Thread.sleep(10 secs) 放在第 2 步和第 3 步之间,则它完全正常。(它在代码中注释)

但是有人能解释一下幕后发生的事情Platform.runLater()吗,为什么总是不工作?

JKo*_*dis 5

Platform 类的文档很好地解释了一切:

public static void runLater(Runnable runnable)
Run Code Online (Sandbox Code Playgroud)

在将来某个未指定的时间在 JavaFX 应用程序线程上运行指定的 Runnable 。该方法可以从任何线程调用,它将把 Runnable 发布到事件队列,然后立即返回给调用者。Runnables 按照发布的顺序执行。传入 runLater 方法的 runnable 将在任何 Runnable 传入后续调用 runLater 之前执行。如果在 JavaFX 运行时关闭后调用此方法,则该调用将被忽略:Runnable 将不会被执行,也不会抛出任何异常。注意:应用程序应避免使用过多待处理的 Runnable 来淹没 JavaFX。否则,应用程序可能会变得无响应。鼓励应用程序将多个操作批量化为更少的 runLater 调用。此外,长时间运行的操作应尽可能在后台线程上完成 ,从而释放 JavaFX 应用程序线程以进行 GUI 操作。

在 FX 运行时初始化之前不得调用此方法。对于扩展 Application 并使用 Java 启动器或 Application 类中的启动方法之一来启动应用程序的标准 JavaFX 应用程序,FX 运行时由启动器在加载 Application 类之前初始化。

因此,使用 runLater 仅更新非 JavaFX 线程上的任何 UI 元素,并将任何繁重的工作留在后台线程上。


MMA*_*ams 5

JavaFX 应用程序在应用程序线程上运行,该线程处理所有 UI 元素。这意味着如果您单击按钮 A 并单击该按钮启动需要 5 秒才能完成的方法 A,然后单击该按钮一秒钟后,您尝试单击启动方法 B 的按钮 B,方法 B 将不会启动,直到方法一个结束。或者可能按钮 B 在方法 A 完成之前甚至无法工作,我对那里的细节有点模糊。

阻止应用程序冻结的一个好方法是使用线程。要解决上述情况,单击按钮 A 将启动启动新线程的方法 A。然后线程可以在不锁定 UI 并阻止您单击按钮 B 的情况下完成它想要完成的时间。

现在,说一些需要在应用程序线程上的方法 A 中的内容,例如,它更新了一个 UI 组件,如标签或文本字段。然后在方法 A 中的线程中,您需要将影响 UI 的部分放入 a 中Platform.runLater(),以便它与 UI 的其余部分一起在应用程序线程上运行。

对于您的示例,这意味着您有两个选择。
1. 根本不要使用线程,因为您不希望用户在更新发生时与 UI 交互。
2.搬出c.updateData()去是Platform.runLater()这样的:

 Runnable task = new Runnable() {
            @Override
            public void run() {
                c.updateData();
                Platform.runLater(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            infoLogTextArea.appendText(c.getErrorLog().toString());
                            plotLabel.setText(c.getCityData().size()+" cities found and updated from internet");
                            infoLogTextArea.appendText("Successfully updated the data from Internet\n");
                        }catch (IOException e) {
                            infoLogTextArea.setText("Couldnot update the data from web: "+e.getMessage()+"\n");
                        }
                        finally {
                            plotButton.setDisable(false);
                            updateButton.setDisable(false);
                        }
                    }
                });
            }
        };
Run Code Online (Sandbox Code Playgroud)

其中任何一个都可以工作,但您现在所做的是在应用程序线程上,然后启动另一个线程,其唯一目的是在应用程序线程上运行某些内容。