【问题标题】:JavaFX Task ending and JavaFX threadingJavaFX 任务结束和 JavaFX 线程
【发布时间】:2014-07-24 03:00:21
【问题描述】:

我今天才刚刚开始学习 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();
}

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

【问题讨论】:

    标签: java multithreading javafx


    【解决方案1】:

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

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

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

    但是,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();
    

    【讨论】:

      猜你喜欢
      • 2012-06-09
      • 2014-05-04
      • 2017-12-17
      • 2016-08-15
      • 1970-01-01
      • 2012-10-03
      • 2014-12-22
      • 2015-06-26
      • 1970-01-01
      相关资源
      最近更新 更多