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

Joe*_*nst 4 java multithreading javafx-8

当用户选择在另一个线程中启动阻止进程的菜单项时,我试图在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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

什么是实际发生的情况是这两个文本更新烧成的阻塞进程完成后,即使我使用Platform.runLater().我究竟做错了什么?

Mus*_*afa 8

最简单的方法是使用a Task.Platform.runLater仅在需要从其他线程更新UI时才需要,因此在您的情况下不需要.如果你想在运行时跟踪后台任务的进度,您可以使用updateMessage,并updateProgress在任务的方法来安全地传递消息到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<Void>() {
                @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();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 在长期存在的应用程序中,最好使用Executors而不是显式的Thread实例化. (2认同)
  • 要实现ursa的建议,您可以使用[服务](https://docs.oracle.com/javase/8/javafx/api/javafx/concurrent/Service.html)而不是任务.服务有一些内置的工具,可以通过执行程序进行线程管理.您也可以将Executors与Tasks配合使用,但Task API不为此提供任何辅助API.使用服务比任务稍微复杂一些. (2认同)