如何从我自己的线程安全地修改 JavaFX GUI 节点



我尝试更改线程中的JavaFX GUI节点,但是看到以下错误:

线程"Thread-8"中的异常 java.lang.IllegalStateException: not on 外汇应用线程;当前线程 = 线程-8

生成错误的示例代码:

public class Controller { 
  public Label label = new Label();
  public void load() {
    MyThread myThread = new MyThread();
    myThread.start();
  }
  public class MyThread extends Thread {
    public void run() {
      ......
      label.setText(""); // IllegalStateException: Not on FX application thread
    }
  }
}

活动场景图中 JavaFX 节点的所有操作都必须在 JavaFX 应用程序线程上运行,否则程序可能无法正常工作。

当您尝试从 JavaFX 应用程序线程中修改场景图节点的属性时,JavaFX 将引发异常IllegalStateException: Not on FX application thread。 即使您没有获得 IllegalStateException,也不应该从 JavaFX 应用程序线程中修改场景图节点,因为如果您这样做,您的代码可能会不可预测地失败。

使用Platform.runLater()

将操作场景图节点的代码包装在 Platform.runLater 调用中,以允许 JavaFX 系统在 JavaFX 应用程序线程上运行代码。

例如,您可以使用以下代码修复示例程序:

Platform.runLater(() -> label.setText(""));

使用具有message属性的Task的替代

方法

如果您使用的是 JavaFX 任务,该任务对使用 JavaFX 的并发编程提供了一些内置支持,则可以利用其消息属性,该属性可以从任何线程安全地更新,但仅在 JavaFX 线程上中继属性更改。

下面是一个示例(来自 Task javadoc):

Task<Integer> task = new Task<Integer>() {
    @Override protected Integer call() throws Exception {
        int iterations;
        for (iterations = 0; iterations < 10000000; iterations++) {
            if (isCancelled()) {
                updateMessage("Cancelled");
                break;
            }
            updateMessage("Iteration " + iterations);
            updateProgress(iterations, 10000000);
        }
        return iterations;
    }
}; 

然后,可以安全地绑定到 message 属性,以使更改的消息值反映在 UI 中:

Label iterationLabel = new Label();
iterationLabel.textProperty().bind(
    task.messageProperty()
);

updateMessage javadoc:

更新消息属性。对更新消息的调用被合并和稍后在 FX 应用程序线程上运行,因此调用 updateMessage,即使从FX应用程序线程,也不一定会导致立即更新此属性和中间消息值可以合并以节省事件通知。

此方法是安全的从任何线程调用。

javadoc Task中有许多使用 updateMessage() 的例子。

最新更新