不在 FX 应用程序线程上;当前线程 = 线程 5

Not on FX application thread; 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.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(BehaviorSkinBase.java:197)
    at com.sun.javafx.scene.control.MultiplePropertyChangeListenerHandler.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(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();
}
}

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

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

Platform.runLater() 有以下 javadoc:

Run the specified Runnable on the JavaFX Application Thread at some unspecified time in the future. This method, which may be called from any thread, will post the Runnable to an event queue and then return immediately to the caller. The Runnables are executed in the order they are posted. A runnable passed into the runLater method will be executed before any Runnable passed into a subsequent call to runLater. If this method is called after the JavaFX runtime has been shutdown, the call will be ignored: the Runnable will not be executed and no exception will be thrown.

NOTE: applications should avoid flooding JavaFX with too many pending Runnables. Otherwise, the application may become unresponsive. Applications are encouraged to batch up multiple operations into fewer runLater calls. Additionally, long-running operations should be done on a background thread where possible, freeing up the JavaFX Application Thread for GUI operations.

This method must not be called before the FX runtime has been initialized. For standard JavaFX applications that extend Application, and use either the Java launcher or one of the launch methods in the Application class to launch the application, the FX runtime is initialized by the launcher before the Application class is loaded. For Swing applications that use JFXPanel to display FX content, the FX runtime is initialized when the first JFXPanel instance is constructed. For SWT application that use FXCanvas to display FX content, the FX runtime is initialized when the first FXCanvas instance is constructed.

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

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 periodic background task