SwingWorker的done()方法释放JDialog时异常行为排查
问题根因
本质是Swing事件调度线程(EDT)的任务执行时序 + 模态JDialog的阻塞特性共同导致的问题,和本地查询速度快直接相关:
- SwingWorker的
publish()方法不会立刻触发process()执行,只是将参数批量打包后异步提交到EDT的任务队列等待调度,done()方法本身同样是提交到EDT队列执行的任务。 - 本地数据库查询仅耗时几毫秒,会出现如下时序冲突:
doInBackground先提交publish(true)到EDT队列- 数据库查询立刻完成,继续提交
publish(false)到EDT队列,随后doInBackground执行结束,done()任务也被提交到EDT队列 - EDT开始执行队列第一个任务:调用
loader.setVisible(true)。模态JDialog的setVisible(true)是阻塞方法,会直接卡住当前EDT线程,直到弹窗被手动关闭,队列里后续的publish(false)、done()任务完全没有执行机会。
- 远程数据库查询耗时久的场景下没有问题,是因为
publish(true)提交后,EDT有足够的时间先执行完setVisible(true)完成弹窗显示,EDT恢复响应后,后续任务可以正常调度执行。
修复方案
方案1(最稳妥):将弹窗显示逻辑移到SwingWorker启动前执行
不要用publish控制弹窗显示,直接在EDT线程中先显示弹窗再启动后台任务,从根源上避免时序冲突:
// 以下代码运行在EDT中 JDialog loader = new MyDialogLoader(); loader.setVisible(true); new SwingWorker<Void, Void>() { @Override protected Void doInBackground() { // 数据库执行逻辑 return null; } @Override protected void done() { loader.dispose(); // UI更新逻辑 } }.execute();
方案2:修改process逻辑,仅执行最新的状态
如果要保留用publish控制的逻辑,不要遍历chunks里的所有值执行,只取最后一个最新的状态,避免旧的true状态覆盖后续的false状态:
@Override protected void process(List<Boolean> chunks) { // 只取队列最后一个最新的状态 Boolean latestState = chunks.get(chunks.size() - 1); loader.setVisible(latestState); }
内容的提问来源于stack exchange,提问作者Salim JB
相关产品推荐
相关产品推荐

