JavaFX任务结束和JavaFX线程

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

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

public GameFrame(){ this.setPrefSize(800, 600); Snake snake = new Snake(); this.getChildren().add(snake); taskThread = new Thread(new Task() { protected Void call() throws Exception { while(!Thread.currentThread().isInterrupted()){ snake.updatePosition(); try{ Thread.sleep(1000); } catch(InterruptedException e){ break; } } return null; } }); taskThread.start(); } 

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

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

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

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

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

在这种情况下,最好使用一个简单的Thread并使用Platform.runLater(...)来更新UI。 Platform.runLater(...)获取Runnable并在FX Application Thread上执行其run()方法。

我不清楚为什么你的代码表现得像你描述的那样,但假设方法调用snake.updatePosition()导致UI的变化,应该在FX Application Thread上执行。 无论如何我会尝试

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