You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaFX中future.get()阻塞UI更新问题求助

问题根源分析

你的核心问题是在JavaFX应用线程(UI线程)里调用了Future.get(),这个方法会直接阻塞当前线程直到任务完成。而Platform.runLater提交的加载器显示逻辑,要等UI线程空闲时才会执行——但你的UI线程被get()死死堵住了,所以加载器只能等call()方法执行完才会显示,完全失去了加载提示的意义。

解决方案:异步处理Task结果,避免阻塞UI线程

我们需要重构代码,去掉UI线程上的阻塞调用,改用Task的回调机制来获取结果,同时保证加载器能及时显示。

1. 重构performQuery方法,返回Future而非直接返回List

把同步等待结果的逻辑去掉,改为返回Future<List>,同时给Task绑定回调来处理结果和UI更新:

@Override 
public Future<List<TaskInfoTableView>> performQuery(boolean isPaginationQuery, boolean isSortingQuery) { 
    TaskImpl taskImpl = new TaskImpl(isPaginationQuery, isSortingQuery); 

    // 任务成功时的回调:处理结果+更新UI
    taskImpl.setOnSucceeded(event -> {
        List<TaskInfoTableView> result = taskImpl.getValue();
        if (!isPaginationQuery) {
            ((TaskInfoViewController) uiController).setPagination(FXCollections.observableArrayList(result));
        }
        // 任务完成后隐藏加载器
        Platform.runLater(() -> loaderContainer.setVisible(false));
    });

    // 任务失败时的回调:处理异常
    taskImpl.setOnFailed(event -> {
        taskImpl.getException().printStackTrace();
        Platform.runLater(() -> loaderContainer.setVisible(false));
    });

    // 任务取消时的回调
    taskImpl.setOnCancelled(event -> {
        System.out.println("Task cancelled!");
        Platform.runLater(() -> loaderContainer.setVisible(false));
    });

    // 提交任务到线程池
    return queryExecutor.submit(taskImpl); 
}

2. 优化TaskImpl类

调整call()方法的资源管理,同时简化生命周期方法的逻辑,专注于加载器的状态控制:

private class TaskImpl extends Task<List<TaskInfoTableView>> { 
    private final boolean isPaginationQuery;
    private final boolean isSortingQuery; 

    public TaskImpl(boolean isPaginationQuery, boolean isSortingQuery) { 
        this.isPaginationQuery = isPaginationQuery; 
        this.isSortingQuery = isSortingQuery; 
    } 

    @Override 
    protected List<TaskInfoTableView> call() throws Exception { 
        // 立即提交加载器显示逻辑到UI线程
        Platform.runLater(() -> { 
            loaderContainer.setVisible(true); 
            loaderContainer.toFront(); 
        }); 

        // 使用try-with-resources自动关闭HSession,避免资源泄漏
        try (HSession hSession = new HSession()) {
            TaskInfoDao taskInfoDao = new TaskInfoDaoImpl(
                hSession.getSession(), 
                currentConnection.getConnectionId()
            ); 

            if (!isPaginationQuery && !isSortingQuery) { 
                paginator.setTotal(
                    taskInfoDao.getTaskInfoWithFiltersCount(paginator.getFilterMap(), false)
                ); 
            } 

            Stream<TaskInfo> resultStream = taskInfoDao.getTaskInfoWithFilters(
                paginator.getFilterMap(), 
                false, 
                paginator.getStartIndex() * paginator.getPageSize(), 
                paginator.getPageSize() * paginator.getPageGap()
            ); 

            return createData(resultStream); 
        }
    } 

    @Override 
    protected void succeeded() { 
        super.succeeded(); 
        updateMessage("Completed!");
    } 

    @Override 
    protected void cancelled() { 
        super.cancelled(); 
        updateMessage("Cancelled!"); 
    } 

    @Override 
    protected void failed() { 
        super.failed(); 
        updateMessage("Failed!"); 
    } 
}

关键注意点

  • 绝对不要在UI线程调用Future.get():如果你的业务逻辑确实需要同步获取结果,必须在后台线程里调用(比如另一个线程池任务),否则会导致UI冻结。
  • 用try-with-resources管理资源:HSession这类需要手动关闭的资源,用try-with-resources可以确保无论是否异常,都会被正确关闭。
  • 加载器的显示/隐藏都要通过Platform.runLater:所有UI操作必须在UI线程执行,即使是在Task的生命周期方法里(这些方法默认也是在UI线程执行,但显式调用更安全)。

如果你需要同步获取结果(非UI场景)

如果在后台线程中需要等待Task完成,可以这样做:

// 注意:这段代码必须在非UI线程执行!
Future<List<TaskInfoTableView>> taskFuture = performQuery(true, true);
List<TaskInfoTableView> result = taskFuture.get(); // 这里阻塞的是后台线程,不影响UI

内容的提问来源于stack exchange,提问作者Anurag Upadhyaya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:57:54