Java Swing中从菜单项ActionListener触发JProgressBar不显示的问题
核心问题根源
Swing 所有UI组件的绘制、事件响应、状态更新操作都由*事件分发线程(EDT)*统一处理,该线程绝对不能被阻塞。
- 菜单项的
actionPerformed回调默认运行在EDT线程上,此时直接调用Process()方法,会把包含Thread.sleep()的循环逻辑全部放在EDT上执行,直接将EDT完全阻塞。 - 被阻塞的EDT无法执行组件布局、绘制任务,因此进度指示器窗口仅能弹出框架,内部的
JProgressBar无法完成渲染;同时通过SwingUtilities.invokeLater提交的进度更新任务,也因为EDT被卡住,根本没有执行机会。 - 直接在Main2构造器中调用
Process()时,整个程序运行在主线程,循环和休眠操作不会占用EDT资源,EDT可以正常完成UI绘制、更新,因此功能表现正常。
修正方案
严格遵循Swing线程规则拆分任务:
- 所有UI组件的创建、显示、状态修改操作,必须提交到EDT执行。
- 所有带循环、休眠、IO的耗时操作,必须放到独立的工作线程运行,不能阻塞EDT。
修正后的Process方法示例
private void Process() { // 进度条初始化、显示逻辑提交到EDT执行 SwingUtilities.invokeLater(() -> { pi = new jpcProgressIndicator(); pi.setTitle("Progress of Growth & Persistency"); pi.Update(0); pi.Show(true); }); // 耗时循环逻辑放到独立工作线程,避免阻塞EDT new Thread(() -> { int nCounter = 0; while (nCounter < 100) { final int currentValue = nCounter; try { // 进度值更新提交到EDT执行 SwingUtilities.invokeLater(() -> pi.Update(currentValue)); Thread.sleep(100); } catch (InterruptedException e) { System.out.println("Error: " + e.getMessage()); } nCounter++; } // 窗口关闭逻辑提交到EDT执行 SwingUtilities.invokeLater(() -> pi.Close()); }).start(); }
生产环境可使用 SwingWorker 替代普通工作线程,其内置的进度发布、任务完成回调机制能更好适配Swing线程模型,减少重复提交UI更新任务的代码量。
内容的提问来源于stack exchange,提问作者Jon
相关产品推荐
相关产品推荐

