JavaFX任务结束和JavaFX线程

时间:2014-07-24 03:00:21

标签: java multithreading javafx

我今天才开始学习JavaFX,我试图通过制作Snake克隆来了解更多信息,但我遇到了线程问题。我想创建一个更新蛇在屏幕上的位置的线程,但是不能以正常的Runnable线程方式使用它,因为我在该线程中使用JavaFX来更新绘制到的矩形的位置屏幕(我学到了你做不到,而且必须使用Tasks,Services,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();
}

我觉得我实际上并没有意识到这里最好的事情是什么,而我想做的事情可能是hackish。对于我应该做什么,或者我如何解决这个问题,有什么建议吗?

1 个答案:

答案 0 :(得分:4)

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

  1. 任何阻止执行(或需要很长时间才能执行)的东西都应该在后台线程上运行 - 而不是在FX Application Thread上运行
  2. 任何改变场景图中Node状态的内容都应该在FX应用程序线程上执行
  3. 为了帮助实现这些目标,JavaFX API提供了Task类。这有一个返回值的call()方法;它是Runnable,因此可以作为Thread构造函数的参数提供,或传递给Executor。它还提供有用的回调,保证在FX应用程序线程上执行,例如setOnSucceededsetOnFailed以及更新update...()等属性的各种progress方法。 FX应用程序主题上的message

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

    在这种情况下,使用简单的Thread并使用Platform.runLater(...)来更新UI可能会更好。 Platform.runLater(...)获取Runnable并在FX应用程序主题上执行其run()方法。

    我不清楚为什么你的代码按照你描述的方式行事,但假设方法调用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;
                    }
                }
        }
    });
    

    如果您使用的是Java 8,那么lambdas取代匿名内部类的效果会更好:

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

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

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

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