首页 > 解决方案 > 不在 FX 应用程序线程上;currentThread = Thread-5

问题描述

Exception in thread "Thread-5" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-5
    at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:279)
    at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:423)
    at javafx.scene.Parent$2.onProposedChange(Parent.java:367)
    at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:113)
    at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:108)
    at com.sun.javafx.scene.control.skin.LabeledSkinBase.updateChildren(LabeledSkinBase.java:575)
    at com.sun.javafx.scene.control.skin.LabeledSkinBase.handleControlPropertyChanged(LabeledSkinBase.java:204)
    at com.sun.javafx.scene.control.skin.LabelSkin.handleControlPropertyChanged(LabelSkin.java:49)
    at com.sun.javafx.scene.control.skin.BehaviorSkinBase.lambda$registerChangeListener$70(BehaviorSkinBase.java:197)
    at com.sun.javafx.scene.control.MultiplePropertyChangeListenerHandler$1.changed(MultiplePropertyChangeListenerHandler.java:55)
    at javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:89)
    at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:182)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
    at javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:103)
    at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:110)
    at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:144)
    at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:49)
    at javafx.beans.property.StringProperty.setValue(StringProperty.java:65)
    at javafx.scene.control.Labeled.setText(Labeled.java:145)
    at edu.controller.GUIController.statusShow(GUIController.java:443)
    at edu.controller.GUIController.lambda$7(GUIController.java:214)
    at java.lang.Thread.run(Unknown Source)

我正在制作一个文本编辑器,我想在页脚中添加一个状态栏,在几秒钟后告诉用户不同的提示,当我尝试在标签上设置文本但尝试设置时,我遇到了这个错误控制台上的文本可以正常工作。

new Thread(()->{
    statusBarShow();
}).start();

private void statusBarShow(){
try {
    statusLable.setText("Tip 1");
    Thread.sleep(2000);
    statusLable.setText("Tip 2");
    Thread.sleep(2000);
    statusLable.setText("Tip 3");
    Thread.sleep(2000);
    statusLable.setText("Tip 4");
    Thread.sleep(2000);
    statusLable.setText("Tip 5");
    Thread.sleep(2000);
} catch (Exception e) {
    e.printStackTrace();
}
}

标签: multithreadingjavafxjavafx-8

解决方案


唯一允许修改 JavaFX GUI 的线程是 JavaFX 线程。如果任何其他线程修改 UI,您将收到异常。

对于这个常见问题,最简单的答案是将更改 GUI 的代码封装在Platform.runLater(). 注意不要在 runlater() 中放置任何休眠,因为它们会导致 JavaFX GUI 线程休眠,这将导致您的程序在休眠期间冻结。

Platform.runLater()有以下javadoc:

在未来某个未指定的时间在 JavaFX 应用程序线程上运行指定的 Runnable。此方法可以从任何线程调用,它将 Runnable 发布到事件队列,然后立即返回给调用者。Runnables 按照它们发布的顺序执行。传递给 runLater 方法的 runnable 将在传递给后续调用 runLater 的任何 Runnable 之前执行。如果在 JavaFX 运行时关闭后调用此方法,则该调用将被忽略:不会执行 Runnable,也不会抛出异常。

注意:应用程序应避免使用太多待处理的 Runnable 来淹没 JavaFX。否则,应用程序可能会变得无响应。鼓励应用程序将多个操作批处理到更少的 runLater 调用中。此外,应尽可能在后台线程上完成长时间运行的操作,从而为 GUI 操作释放 JavaFX 应用程序线程。

在初始化 FX 运行时之前不得调用此方法。对于扩展 Application 并使用 Java 启动器或 Application 类中的一种启动方法来启动应用程序的标准 JavaFX 应用程序,FX 运行时由启动器在加载 Application 类之前初始化。对于使用 JFXPanel 显示 FX 内容的 Swing 应用程序,在构造第一个 JFXPanel 实例时初始化 FX 运行时。对于使用 FXCanvas 显示 FX 内容的 SWT 应用程序,FX 运行时在构造第一个 FXCanvas 实例时被初始化。

我认为您的代码的结构不是完成此任务的最佳方式,但一个非常简单的解决方案如下:

new Thread(()->{
    statusBarShow();
}).start();

private void statusBarShow(){
    try {
        Platform.runLater(()->statusLable.setText("Tip 1"));
        Thread.sleep(2000);
        Platform.runLater(statusLable.setText("Tip 2"));
        Thread.sleep(2000);
        Platform.runLater(statusLable.setText("Tip 3"));
        Thread.sleep(2000);
        Platform.runLater(statusLable.setText("Tip 4"));
        Thread.sleep(2000);
        Platform.runLater(statusLable.setText("Tip 5"));
    } catch (Exception e) {
        e.printStackTrace();
    }
}

解决您的问题的更好方法可能是使用AnimationTimer

这是一个关于如何完成的有用线程:JavaFX 定期后台任务


推荐阅读