后台线程仍然直接访问UI

时间:2019-04-12 10:31:15

标签: javafx

这是我的代码,有人可以解释为什么每次都起作用吗?

package dingding;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;


public class Dingding extends Application {

    TextField tfAuto = new TextField("0");
    AutoRunThread runner = new AutoRunThread();
    boolean shouldStop = false;

    private class AutoRunThread extends Thread {

        @Override
        public void run() {
            while (true) {
                int i = Integer.parseInt(tfAuto.getText());
                ++i;
                tfAuto.setText(String.valueOf(i));
                try {
                Thread.sleep(1000);
                } catch (Throwable t) {

                }
                if (shouldStop) {
                    runner = null;
                    shouldStop = false;
                    return;
                }
            }
        }
    }

    @Override
    public void start(Stage primaryStage) {

        Button btnStart = new Button("Increment Automatically");
        Button btnStop = new Button("Stop Autotask");

        btnStart.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                if (runner == null) {
                    runner = new AutoRunThread();
                    runner.setDaemon(true);
                }
                if (runner != null && !(runner.isAlive())) {
                    runner.start();
                }
            }
        });

        btnStop.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                shouldStop = true;
            }
        });


        VBox rootBox = new VBox();
        HBox autoBox = new HBox();

        autoBox.getChildren().addAll(tfAuto, btnStart, btnStop);

        rootBox.getChildren().addAll(autoBox);

        Scene scene = new Scene(rootBox, 300, 250);

        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }

}

1 个答案:

答案 0 :(得分:3)

正如我在评论中所说,不正确的同步代码不能保证错误本身。但是,这并不意味着所说的代码在多线程上下文中使用时实际上可以工作-您只是幸运。最终,您将遇到未定义的行为,例如损坏的状态,陈旧的值和意外的异常。这是因为,如果没有同步,则不能保证一个线程执行的操作对其他任何线程都是可见的。您需要先发生先发生的关系,在java.util.concurrentpackage documentationthis SO question中有更好的描述。

与大多数UI框架/工具包一样,JavaFX是单线程的。这意味着有一个特殊的线程(在本例中为 JavaFX Application Thread )负责所有与UI相关的操作 1 。必须使用此线程(仅此线程)来访问和/或修改与“实时”场景图相关的状态(即,场景中位于显示 2 )。使用任何其他线程都可能导致上述未定义行为

一些与UI相关的函数实际上确保在 JavaFX Application Thread 上调用它们,如果没有,通常会抛出IllegalStateException。但是,其余的函数将使您无声地从任何线程中调用它们,但这并不意味着这样做是安全的。我相信,这样做是通过这种方式完成的,因为检查每个与UI相关的功能中的线程是维护的噩梦,并且会带来不小的影响。


1。这有点复杂。 JavaFX还具有“棱镜渲染线程”和“媒体线程”。有关更多信息,请参见Understanding JavaFX Architecture。但是请注意,从应用程序开发人员的角度来看,唯一重要的线程是 JavaFX Application Thread

2。 Node对此进行了说明。请注意,某些节点(例如WebView)在线程化方面更具限制性。这将记录在适当的位置。