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

时间:2014-11-06 14:04:51

标签: java multithreading javafx

我最近遇到了这个问题:

java.util.NoSuchElementException 线程“Thread-4”中的异常java.lang.IllegalStateException:不在FX应用程序线程上; currentThread = Thread-4

代码适用于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());
    }
}

然后我从这段代码中调用这个帖子:

    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);
        }
    }

我想要做的就是从其他具有函数的线程更改控件的值/属性,就像在我的示例中一样,它扫描下一个字符串然后将它附加到TextArea。

为什么它适用于jdk7但不适用于jdk8?请。解释...

我已经完成了一些研究和解决方案,我来到这个JavaFX任务和服务,但是,我没有得到它们,并且没有在线的好例子。

请帮忙......谢谢......

2 个答案:

答案 0 :(得分:4)

您的代码实际上也在JDK7上被破坏了。从后台线程(即不是来自FX应用程序线程)更改UI控件的状态是非法的。与几乎所有UI工具包一样,JavaFX使用单线程模型,并且不提供UI元素上的同步。因此,如果从FX Application Thread以外的线程更改或查看UI控件的状态,则代码很容易出现未指定和不可预测的行为。

在JavaFX 2.2及更早版本(随JDK 7提供)中,对于您的代码是否在正确的线程上执行的运行时检查较少。因此,您不会在JDK 7中获得运行时异常,但如上所述,您的代码存在错误,结果不确定。在JavaFX 8中,API得到了改进,因此(在大多数情况下)违反此规则会引发IllegalStateException。 (这更好,因为你马上就知道出了什么问题。)

修复方法是更新FX应用程序线程上的UI控件:

final TextArea ta = new TextArea() ;

// ...

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

如果您现在只使用Java 8,则可以使用lambda表达式替换匿名内部类:

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

(并且不再需要声明ta作为最终版,尽管仍有限制。)

答案 1 :(得分:2)

JavaFX场景图的所有更改都必须在FX-Application-Thread上进行。这意味着当你定义自己的线程时,它做了一些工作,然后想要将它的工作呈现给UI,你必须在FX-Application-Thread上做呈现部分。

在您的情况下,实现此目的的最简单方法是在Runnable中移动UI-Update部分(将结果附加到textarea),该部分必须从FX-Application-Thread启动。这可以通过在Platform.runLater()中调用新定义的Runnable来完成:

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

老实说,我不确定,为什么你的方法在Java7中有效,因为afaik,这个行为没有任何改变。要获得有关JavaFX Tasks的更多信息,您可以查看Documentation