即使我使用并发方法,使用 JavaFX 的应用程序仍然冻结

问题描述 投票:0回答:1

我正在尝试在 JavaFX 中创建一个应用程序,该应用程序将从我的 SQLITE 数据库检索数据并显示它。但即使我尝试使用

Task
Platform.runLater()
并使用
Thread.sleep()
模拟加载,它仍然会冻结我的应用程序。我是 javaFX 并发新手,但我正在尝试阅读文档(JavaFXDocs),但似乎找不到我的解决方案。

这是代码

    @FXML
    private void showAllData() throws SQLException, IOException {
        Task<Void> task = dataShowAsATask();
        Thread thread = new Thread(task);
        thread.setDaemon(true);
        thread.start();
    }

    private Task<Void> dataShowAsATask() {
        return new Task<Void>() {
            @Override
            protected Void call() throws Exception {
                Platform.runLater(() -> {
                    try {
                        clearDataResultHolder();
                        dataResultHolder.getChildren().addAll(getArrayOfUserAsNode());

                        /* Simulate loading */
                        Thread.sleep(2 * 1000);
                    } catch (SQLException | IOException | InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                });
                return null;
            }
        };
    }

    private Node getUserAsANode(User user) throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/org/prince/firstcrudtest/views/card/CardSkeleton.fxml"));
        Node data = fxmlLoader.load();
        CardSkeletonController controller = fxmlLoader.getController();
        setController(controller, user);
        return data;
    }

    private AbstractList<Node> getArrayOfUserAsNode() throws SQLException, IOException {
        ArrayList<Node> result = new ArrayList<>();

        ArrayList<User> data = dbNav.getAllData();
        for (User user : data) {
            result.addLast(getUserAsANode(user));
        }

        return result;
    }

    private void setController(CardSkeletonController controller, User user) {
        controller.setIdLabel(String.valueOf(user.getId()));
        controller.setUsernameLabel(user.getUserName());
        controller.setPasswordLabel(Arrays.toString(user.getPassword()));
        controller.setSaltLabel(Arrays.toString(user.getSalt()));
    }

    private void clearDataResultHolder() {
        dataResultHolder.getChildren().clear();
    }

我真的不知道该怎么办,因为我是 JavaFX 和 Java 线程的新手。

java multithreading javafx
1个回答
0
投票

在非 JavaFX 应用程序线程的线程中执行耗时操作的原因是它们会延迟 JavaFX 事件的处理,包括渲染场景以及处理鼠标和键盘输入。

Platform.runLater 专门用于在 JavaFX 应用程序线程中运行任务。使用它来更新场景(这是clearDataResultHolder()和dataResultHolder.getChildren().addAll(getArrayOfUserAsNode())所做的)是正确的。

但是,在JavaFX应用程序线程中调用Thread.sleep是有害的。这就是冻结您的申请的原因。

基于执行长时间运行操作的任务创建一个新线程是很好的,因为该新线程不是 JavaFX 应用程序线程,并且不会挂起应用程序线程。您应该在该任务中执行 Thread.sleep before 调用 Platform.runLater:

@Override
protected Void call() throws Exception {
    // Does the time-consuming work in this background thread,
    // NOT in the application thread.
    /* Simulate loading */
    Thread.sleep(2 * 1000);

    // Updates the user interface in the JavaFX application thread,
    // because updating it in this background thread is not allowed.
    Platform.runLater(() -> {
        clearDataResultHolder();
        dataResultHolder.getChildren().addAll(getArrayOfUserAsNode());
    });
    return null;
}

如果您实际上执行数据库操作,而不是仅仅调用 Thread.sleep,则代码结构将是相同的:在任务线程中执行工作,然后调用 Platform.runLater 用您检索到的数据更新场景。

© www.soinside.com 2019 - 2024. All rights reserved.