如何让主线程等待事件分发线程创建的GUI关闭后再执行?
问题分析
你的核心问题在于:GUIMaker线程并没有真正等待GUI关闭——它只是把GUI创建的任务**提交给事件分发线程(EDT)**后就立刻执行完毕了,所以TFM.join()等待的是这个"提交任务"的线程结束,而不是GUI窗口关闭。这就是为什么GUI弹出后,主线程马上就显示了"GM Done"对话框。
解决方案
针对这个需求,有两种常用的可靠方式,推荐优先使用第一种(更符合Swing的设计规范):
方案1:使用模态对话框(最简洁)
Swing的模态对话框(JDialog设置为模态)本身就具备阻塞调用线程的特性,只要正确在EDT中显示,就能让主线程等待直到对话框关闭。
修改GUIMaker的代码:
@Override public void run() { try { // 用invokeAndWait替代invokeLater,让当前线程等待EDT完成GUI创建和显示 SwingUtilities.invokeAndWait(new Runnable() { public void run() { UIManager.put("swing.boldMetal", Boolean.FALSE); CreateAndShowModalGUI(); } }); } catch (InterruptedException | InvocationTargetException e) { e.printStackTrace(); } } private void CreateAndShowModalGUI() { // 创建模态对话框,第三个参数为true表示模态 JDialog dialog = new JDialog((Frame) null, "我的GUI", true); // 添加OK按钮,点击后关闭对话框 JButton okButton = new JButton("OK"); okButton.addActionListener(e -> dialog.dispose()); dialog.add(okButton); dialog.pack(); dialog.setLocationRelativeTo(null); dialog.setVisible(true); // 这里会阻塞EDT,直到对话框关闭 }
修改初始类的方法:
private void CreateAndRunThread(){ GUIMaker GM = new GUIMaker(data); Thread TFM = new Thread(GM); // 注意:你之前的代码里TFM变量应该是这里创建的线程 TFM.start(); try { TFM.join(); // 现在TFM线程会等待EDT中的模态对话框关闭才结束,join生效 } catch (InterruptedException e) { e.printStackTrace(); } MessageDialog.showDialog("GM Done"); }
原理:invokeAndWait会让GUIMaker线程等待EDT完成所有任务,而模态对话框在EDT中显示时会阻塞EDT直到关闭,所以GUIMaker线程会一直等到GUI关闭才结束,主线程的join()自然就能等待到GUI关闭的时机。
方案2:使用CountDownLatch同步工具
如果必须使用JFrame而非模态对话框,可以用CountDownLatch来手动同步主线程和GUI关闭事件:
修改GUIMaker类:
import java.util.concurrent.CountDownLatch; public class GUIMaker implements Runnable { private final Data data; private final CountDownLatch closeLatch; // 构造方法传入latch public GUIMaker(Data data, CountDownLatch closeLatch) { this.data = data; this.closeLatch = closeLatch; } @Override public void run() { SwingUtilities.invokeLater(new Runnable() { public void run() { UIManager.put("swing.boldMetal", Boolean.FALSE); CreateAndShowGUI(); } }); } private void CreateAndShowGUI() { JFrame frame = new JFrame("我的GUI"); JButton okButton = new JButton("OK"); okButton.addActionListener(e -> { frame.dispose(); closeLatch.countDown(); // GUI关闭时触发latch计数减1 }); frame.add(okButton); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }
修改初始类的方法:
import java.util.concurrent.CountDownLatch; private void CreateAndRunThread(){ CountDownLatch closeLatch = new CountDownLatch(1); // 初始化计数为1 GUIMaker GM = new GUIMaker(data, closeLatch); Thread TFM = new Thread(GM); TFM.start(); try { closeLatch.await(); // 主线程等待,直到latch计数变为0(即GUI关闭) } catch (InterruptedException e) { e.printStackTrace(); } MessageDialog.showDialog("GM Done"); }
原理:CountDownLatch是一个同步工具,主线程调用await()会阻塞,直到其他线程调用countDown()将计数减到0。这里我们在GUI关闭的按钮事件中调用countDown(),就能让主线程等待直到GUI关闭。
关键注意点
- 永远遵循Swing线程规则:所有Swing组件的创建、修改、显示必须在EDT中执行,所以
invokeLater/invokeAndWait是必不可少的。 - 模态对话框是Swing中实现"等待GUI关闭"需求的标准方式,优先使用,避免手动同步的复杂度。
内容的提问来源于stack exchange,提问作者Zyzyx
相关产品推荐
相关产品推荐

