简体   繁体   English

JavaFX在jdk7上工作正常,但在jdk8上工作不正常:在FX应用程序线程上不工作

[英]JavaFX works fine on jdk7 but not on jdk8: Not on FX application thread

I recently encountered this problem: 我最近遇到了这个问题:

java.util.NoSuchElementException Exception in thread "Thread-4" java.lang.IllegalStateException: Not on FX application thread; java.util.NoSuchElementException线程“ Thread-4”中的异常java.lang.IllegalStateException:不在FX应用程序线程上;仅在FX应用程序线程上有效。 currentThread = Thread-4 currentThread =线程4

The code works on jdk7 but not on jdk8, the code look like this: 该代码适用于jdk7,但不适用于jdk8,该代码如下所示:

public class myThread extends Thread {
    TextArea ta;
    String foo;
    connect(String foo, TextArea ta){
        this.ta = ta;
        this.foo = foo;
    }
    @Override
    public void run{
        Scanner scanner = new Scanner(foo);
        scanner.next();
        ta.appendText(scanner.next());
    }
}

Then i call this thread from this code: 然后我从以下代码中调用此线程:

    public class myApp extends Application {

        @Override
        public void start(Stage stage) {
            TextArea ta = new TextArea();
            TextField tf = new TextField
            Pane root = new Pane();
            root.getChildren().addAll(ta,tf);
            Scene scene = new Scene(root);
            stage.setScene(scene);
            stage.show();

            tf.setOnAction((javafx.event.ActionEvent event) -> {
                String foo = tf.getText();
                tf.clear();
                new myThread(foo,ta).start();
            });
        }

        public static void main(String[] args) {
            launch(args);
        }
    }

All I want to do is change the values/properties of the controls from other threads which have functions just like in my example it scans for the next string then append it to the TextArea. 我要做的就是从其他具有功能的线程中更改控件的值/属性,就像在我的示例中一样,它扫描下一个字符串,然后将其附加到TextArea中。

Why it works on jdk7 but not on jdk8? 为什么它可以在jdk7上运行但不能在jdk8上运行? pls. explain... 说明...

I've done some research and the solution i came up to this JavaFX task and service but, i dont get them and there are no good examples online. 我已经进行了一些研究,并找到了解决JavaFX任务和服务的解决方案,但是我没有得到它们,网上也没有很好的例子。

Pls help... Thanks... 请帮助...谢谢...

Your code is actually broken on JDK7 too. 您的代码实际上在JDK7上也被破坏了。 It is illegal to change the state of UI controls from a background thread (ie not from the FX Application Thread). 从后台线程(即不是从FX Application Thread)更改UI控件的状态是非法的。 Like almost all UI toolkits, JavaFX uses a single threaded model, and provides no synchronization on UI elements. 与几乎所有UI工具包一样,JavaFX使用单线程模型,并且不提供UI元素的同步。 Consequently if you change or view the state of a UI control from a thread other than the FX Application Thread, your code is prone to unspecified and unpredictable behavior. 因此,如果您从FX Application Thread之外的其他线程更改或查看UI控件的状态,则您的代码容易发生未指定且不可预测的行为。

In JavaFX 2.2 and earlier (shipping with JDK 7), there were fewer runtime checks on whether your code was being executed on the correct thread. 在JavaFX 2.2和更早版本(与JDK 7一起提供)中,较少的运行时检查来检查代码是否在正确的线程上执行。 So you don't get the runtime exception in JDK 7, but as mentioned your code is buggy and the results are not deterministic. 因此,您不会在JDK 7中遇到运行时异常,但是如上所述,您的代码有错误,并且结果不确定。 In JavaFX 8, the API was improved so that (in most cases) violating this rule throws an IllegalStateException . 在JavaFX 8中,对API进行了改进,以便(在大多数情况下)违反此规则将引发IllegalStateException (This is better as you know immediately something is wrong.) (这更好,因为您立即知道出了点问题。)

The fix is to update your UI controls on the FX Application Thread: 解决方法是在FX Application Thread上更新UI控件:

final TextArea ta = new TextArea() ;

// ...

Platform.runLater(new Runnable() {
    @Override
    public void run() {
        ta.appendText(scanner.next());
    }
});

If you are now using Java 8 exclusively, you can replace the anonymous inner class with a lambda expression: 如果现在仅使用Java 8,则可以使用lambda表达式替换匿名内部类:

Platform.runLater(() -> ta.appendText(scanner.next()));

(and there's no longer any need to declare ta as final, though there are still restrictions). (尽管仍然有一些限制,但不再需要将ta声明为final了)。

All changes to the scene graph of JavaFX have to happen on the FX-Application-Thread. JavaFX场景图的所有更改都必须在FX-Application-Thread上进行。 This means when you define your own thread, which does some work and then wants to present its work to the UI, you have to do the presenting part on the FX-Application-Thread. 这意味着,当您定义自己的线程时,它会做一些工作,然后想要将其工作呈现给UI,则必须在FX-Application-Thread上进行呈现。

In your case the easiest way to achieve this is moving the UI-Update part (appending the result to the textarea) within a Runnable, which has to be started from the FX-Application-Thread. 在您的情况下,最简单的方法是在可运行对象中移动UI更新部分(将结果追加到文本区域),该部分必须从FX-Application-Thread中启动。 This can be done by calling the new defined Runnable within Platform.runLater(): 这可以通过在Platform.runLater()中调用新定义的Runnable来完成:

Platform.runLater( () -> ta.appendText(scanner.next()) );

Honestly I am not sure, why your approach has worked in Java7, since afaik, nothing changed concerning this behaviour. 坦率地说,我不确定您的方法为何在Java7中有效,因为afaik对此行为没有任何改变。 To get more Information about JavaFX Tasks you can have a look at the Documentation . 要获取有关JavaFX Tasks的更多信息,请查看文档

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM