首页 > 解决方案 > SwingWorker get() 冻结接口

问题描述

我正在使用 SwingWorker 在后台完成一些长时间运行的数据库调用。在执行 SwingWorker 之前,我启动了一个带有标签的 jpanel,以通知用户该应用程序正在执行一些后台工作。问题是,如果我使用 SwingWorker get() 方法来获取长时间运行的操作的结果,我的 jpanel 会冻结并且什么都不会显示。如果我不使用 get() 方法,jpanel 会正常显示。

你能告诉我我可能做错了什么吗?我已经花了几个小时没有弄清楚为什么。SwingWorker 在所有 jpanel 创建完成后被执行,我不明白它为什么会挂起。

非常感谢!

创建 jpanel 并启动 SwingWorker 的函数:

private void snapshotOldVariables() {

    JFrame loadingJFrame = new JFrame();
    LoadingJPanel loadingJPanel = new LoadingJPanel();

    Dimension d1 = new Dimension();
    d1.setSize(500, 500);
    loadingJFrame.setResizable(false);
    loadingJFrame.setMinimumSize(d1);
    loadingJFrame.setTitle("Loading...");
    loadingJFrame.setLocationRelativeTo(null);
    loadingJFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

    loadingJFrame.add(loadingJPanel);
    loadingJFrame.setVisible(true);

    loadingJPanel.updateStatus("Creating snapshot of values...");

    MySwingWorker worker = new MySwingWorker(tableRows, selectedItems);
    worker.execute();

    GMSnapshotVO snap = null;
    try {
        snap = worker.get();
    } catch (InterruptedException | ExecutionException e) {
        System.out.println(e);
    }

    loadingJPanel.updateStatus("Complete");
    loadingJFrame.dispose();


    productIds = snap.getProductIds();
    oldLocationIds = snap.getOldLocationIds();
    oldStatusIds = snap.getOldStatusIds();

}

jpanel 的屏幕截图在函数等待 worker.get() 时冻结: 在此处输入图像描述

带有注释 worker.get() 代码块的函数: private void snapshotOldVariables() {

    JFrame loadingJFrame = new JFrame();
    LoadingJPanel loadingJPanel = new LoadingJPanel();

    Dimension d1 = new Dimension();
    d1.setSize(500, 500);
    loadingJFrame.setResizable(false);
    loadingJFrame.setMinimumSize(d1);
    loadingJFrame.setTitle("Loading...");
    loadingJFrame.setLocationRelativeTo(null);
    loadingJFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

    loadingJFrame.add(loadingJPanel);
    loadingJFrame.setVisible(true);

    loadingJPanel.updateStatus("Creating snapshot of values...");

    MySwingWorker worker = new MySwingWorker(tableRows, selectedItems);
    worker.execute();

    /* COMMENT STARTS HERE
    GMSnapshotVO snap = null;
    try {
        snap = worker.get();
    } catch (InterruptedException | ExecutionException e) {
        System.out.println(e);
    }

    loadingJPanel.updateStatus("Complete");
    loadingJFrame.dispose();

    productIds = snap.getProductIds();
    oldLocationIds = snap.getOldLocationIds();
    oldStatusIds = snap.getOldStatusIds();
    COMMENT ENDS HERE */

}

jpanel 的屏幕截图未冻结并正确显示: 在此处输入图像描述

标签: javaswingjpanelswingworker

解决方案


据我所知,除非您确定它已完成处理,否则不应直接从 UI 线程调用SwingWorker'方法。get()相反,您应该在 overriden 中调用它MySwingWorker.done(),您可以确保后台任务已完成执行。

无关紧要的是,所有JPanel创建都在阻塞之前完成,因为 Swing 仍然需要其主 UI 线程来重新绘制和更新 UI 并保持响应。通过调用get()你正在阻止它。

您应该从 UI 线程调用execute()并且所有结果处理(productIds = snap.getProductIds(); oldLocationIds = snap.getOldLocationIds(); oldStatusIds = snap.getOldStatusIds();在您的情况下)都应该在done()回调中完成。

这个答案中有一个简单的例子。

在你的情况下,它应该是这样的:

//inside MySwingWorker
@Override
protected void done() {
    try {
        System.out.println("My long running database process is done. Now I can update UI without blocking/freezing it.");
        GMSnapshotVO snap = get();
        loadingJPanel.updateStatus("Complete");
        loadingJFrame.dispose();
        productIds = snap.getProductIds();
        oldLocationIds = snap.getOldLocationIds();
        oldStatusIds = snap.getOldStatusIds();
        //..do other stuff with ids
    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    }
}

希望能帮助到你。


推荐阅读