为什么线程会阻塞我的JavaFX UI线程?

当用户选择在另一个线程中启动阻止进程的菜单项时,我试图在JavaFX 8应用程序中提供反馈。 在我的实际应用程序中,它是一个文件下载,但我通过示例使用最少的代码创建了一个测试用例:

import javafx.application.Application; import javafx.application.Platform; import javafx.scene.Scene; import javafx.scene.control.MenuButton; import javafx.scene.control.ToolBar; import javafx.scene.control.MenuItem; import javafx.stage.Stage; public class BlockingThreadTestCase extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) throws Exception { MenuItem menuItem = new MenuItem("Start"); MenuButton menuButton = new MenuButton(); menuButton.setText("Async Process"); menuButton.getItems().addAll(menuItem); menuItem.setOnAction(event -> { menuButton.setText("Running..."); Platform.runLater(() -> { try { // Simulate a blocking process Thread.sleep(5000); } catch (Exception e) { e.printStackTrace(); } menuButton.setText(menuButton.getText() + "Done!"); }); }); final ToolBar toolbar = new ToolBar(menuButton); final Scene scene = new Scene(toolbar); primaryStage.setScene(scene); primaryStage.setWidth(150); primaryStage.show(); } } 

这是它应该如何工作:当您选择“开始”菜单项时,主菜单文本应立即更改为“正在运行…”,然后它应附加“完成!” 在模拟我的文件下载的5秒睡眠之后。

实际发生的是,即使我使用的是Platform.runLater() ,文本更新也会阻塞过程完成触发。 我究竟做错了什么?

最简单的方法是使用Task 。 只有在需要从其他线程更新UI时才需要Platform.runLater ,因此在您的情况下不需要。 如果要在后台任务运行时跟踪后台任务的进度,可以在任务中使用updateMessageupdateProgress方法将消息安全地传递给UI线程,而不必担心通过EDT进行调度。 您可以在https://docs.oracle.com/javase/8/javafx/interoperability-tutorial/concurrency.htm找到更多相关信息。

请参阅下面的最小工作示例。

 import javafx.application.Application; import javafx.concurrent.Task; import javafx.scene.Scene; import javafx.scene.control.MenuButton; import javafx.scene.control.MenuItem; import javafx.scene.control.ToolBar; import javafx.stage.Stage; public class BlockingThreadTestCase extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) throws Exception { MenuItem menuItem = new MenuItem("Start"); MenuButton menuButton = new MenuButton(); menuButton.setText("Async Process"); menuButton.getItems().addAll(menuItem); menuItem.setOnAction(event -> { menuButton.setText("Running..."); Task task = new Task() { @Override public Void call() { //SIMULATE A FILE DOWNLOAD try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } return null; } }; task.setOnSucceeded(taskFinishEvent -> menuButton.setText(menuButton.getText() + "Done!")); new Thread(task).start(); }); final ToolBar toolbar = new ToolBar(menuButton); final Scene scene = new Scene(toolbar); primaryStage.setScene(scene); primaryStage.setWidth(150); primaryStage.show(); } }