更改游标而不是并发

时间:2015-09-25 09:58:32

标签: multithreading javafx cursor task sequential

据我了解,您需要更改call()的{​​{1}}方法中的光标才能正确更改光标。但在我的应用程序中,我按下按钮并做了一些事情。在完成此操作之前,我不希望应用程序执行任何操作。我只想通过更改光标通知用户应用程序正在加载。

问题是如果我为任务启动一个单独的线程,程序的其余部分就不会等待该任务成功。如果我把它放在与应用程序相同的线程中(我想这就是我正在做的事情),通过执​​行Task,光标不会改变。

按下按钮时调用的方法是void方法,因此可以防止我将return语句放在任务Platform.runLater(task)方法中。

最佳方案是我能做到这一点:

setOnSucceeded()

或者只是创建一个任务并在当前线程中执行它,如下所示:

//app.getPrimaryStage() returns the application stage.

void method () throws InterruptedException {
    app.getPrimaryStage().getScene().setCursor(Cursor.WAIT);

    // Simulate execution of code that takes some time
    Thread.sleep(3000);

    app.getPrimaryStage().getScene().setCursor(Cursor.DEFAULT);

}

但这些方法都不起作用。我可以看到光标正在变化&#34;在&#34;下面通过执行此操作://app.getPrimaryStage() returns the application stage. void method () throws InterruptedException { Task<Void> task = new Task<Void>() { protected Void call() { app.getPrimaryStage().getScene().setCursor(Cursor.WAIT); // Simulate execution of code that takes some time Thread.sleep(3000); app.getPrimaryStage().getScene().setCursor(Cursor.DEFAULT); return null; } }; Platform.runLater(task); } 但除非我在单独的线程中运行任务,否则更改不会显示。为什么是这样?什么是强制程序等待由单独线程运行的任务的最佳方法?

1 个答案:

答案 0 :(得分:3)

您需要遵循两条关于JavaFX中多线程的规则:

  1. 对UI 的更改必须在FX应用程序主题上执行
  2. 长期运行的任务不得在FX应用程序主题上执行
  3. 通常,您应该使用Task在后​​台线程上执行代码,即不在FX应用程序线程上执行代码。

    您的代码块都违反了第二条规则:第一条规则是因为您直接在FX应用程序主题上调用了Thread.sleep(...)。在第二个代码块中,您将该调用放在Task中,然后使用Platform.runLater(...)在FX应用程序线程上显式执行任务,这使得它(或多或少)等同于第一个块(即它使任务变得多余)。

    你的基本前提是错误的:你不想&#34;阻止程序&#34;通过阻止FX应用程序线程的执行。这将使UI无响应并阻止任何UI更新(如更改光标)实际发生。你应该&#34;阻止程序&#34;通过禁用它(或它的一部分),并在任务完成时重新启用它。

    所以你想要这样的东西:

    void method() {
        Scene scene = app.getPrimaryStage().getScene();
        Task<Void> task = new Task<Void>() {
            @Override
            public void call() throws Exception {
    
                // Simulate execution of code that takes some time
                Thread.sleep(3000);
    
                return null ;
            }
        };
    
        task.setOnSucceeded(e -> {
            scene.getRoot().setDisable(false);
            scene.setCursor(Cursor.DEFAULT);
        });
    
        scene.getRoot().setDisable(true);
        scene.setCursor(Cursor.WAIT);
    
        Thread t = new Thread(task);
        t.setDaemon(true);
        t.start();
    }