JavaFX 任务结束和 JavaFX 线程

1 java multithreading javafx

我今天才刚刚开始学习 JavaFX,我试图通过制作 Snake 克隆来了解更多关于它的信息,但我在线程处理方面遇到了麻烦。我想创建一个线程来更新蛇在屏幕上的位置,但不能以正常的 Runnable 线程方式使用一个线程,因为我在该线程中使用 JavaFX 来更新绘制到屏幕上的矩形的位置(这是我学到的)你不能做,而必须使用任务、服务、Platform.runLater 等?)我正在从扩展 JavaFX.scene.layout.Pane 创建线程的类,我正在尝试使用任务来更新蛇的位置. 我的问题是:任务似乎只运行了一次或两次就退出了,我没有给出任何中断。

扩展 Pane 的类的构造函数(Snake 类扩展了 Group):

public GameFrame(){
    this.setPrefSize(800, 600);

    Snake snake = new Snake();
    this.getChildren().add(snake);

    taskThread = new Thread(new Task<Void>() { 
        protected Void call() throws Exception {
                while(!Thread.currentThread().isInterrupted()){
                    snake.updatePosition();
                    try{
                    Thread.sleep(1000);
                    } catch(InterruptedException e){
                        break;
                    }
                }
            return null;
        }
    });
    taskThread.start();
}
Run Code Online (Sandbox Code Playgroud)

我觉得我实际上并没有掌握在这里做的最好的事情是什么,而且我正在尝试做的事情可能是骇人听闻的。对我应该做什么或如何解决这个问题有什么建议吗?

Jam*_*s_D 5

JavaFX 中线程的基本规则(如果你已经理解了其中的一些,请原谅我,我只是想完整一点)是:

  1. 任何阻止执行(或需要很长时间才能执行)的东西都应该在后台线程上运行——而不是在 FX 应用程序线程上
  2. 任何改变作为Node场景图一部分的状态的东西都应该在 FX 应用线程上执行

为了帮助实现这些,JavaFX API 提供了一个Task类。这有一个call()返回值的方法;它Runnable可以作为Thread构造函数的参数提供,或传递给Executor. 它还提供了保证将要在FX应用程序线程执行有用回调,如setOnSucceededsetOnFailed和各种update...()方法该更新属性,如progressmessage对FX应用程序线程。

但是,Task该类实际上是为一次性任务设计的:例如,考虑需要从数据库中检索数据的应用程序,这可能需要时间。这些执行特定操作并返回结果。您的情况有些不同,因为您的线程正在连续执行。

在这种情况下,最好使用 simple Threadand usePlatform.runLater(...)来更新 UI。Platform.runLater(...)接受 aRunnablerun()在 FX 应用程序线程上执行其方法。

我不清楚为什么您的代码会按照您描述的方式运行,但假设方法调用snake.updatePosition()会导致 UI 发生更改,那么应该在 FX 应用程序线程上执行该更改。无论如何我会尝试

taskThread = new Thread(new Runnable() { 
    public void run() {
            while(!Thread.currentThread().isInterrupted()){
                Platform.runLater(new Runnable() {
                    @Override
                    public void run() {
                        snake.updatePosition();
                    }
                });
                try{
                    Thread.sleep(1000);
                } catch(InterruptedException e){
                    break;
                }
            }
    }
});
Run Code Online (Sandbox Code Playgroud)

如果您使用的是 Java 8,那么使用 lambdas 替换匿名内部类后,这一切看起来好多了:

taskThread = new Thread( () -> {
    while (! Thread.currentThread().isInterrupted()) {
        Platform.runLater( snake::updatePosition );
        try {
            Thread.sleep(1000);
        } catch (InterruptedException exc) {
            break ;
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

JavaFX 中用于定期执行某事的另一种技术是(ab?)使用动画:

    Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1), 
        new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                snake.updatePosition();
            }
        }
    ));
    timeline.setCycleCount(Animation.INDEFINITE);
    timeline.play();
Run Code Online (Sandbox Code Playgroud)

或者,在 Java 8 中,有点圆滑

    Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1), 
        event -> snake.updatePosition()));
    timeline.setCycleCount(Animation.INDEFINITE);
    timeline.play();
Run Code Online (Sandbox Code Playgroud)