Java Swing中如何阻止线程重复打开模态JDialog?
Java Swing 模态对话框重复打开问题
我在Java Swing中有两个模态对话框:主对话框(Main dialog)和表格对话框(Tables dialog)。表格对话框可通过按钮手动点击打开,也能通过线程每隔X秒自动打开。无人操作时,线程能正常按上次设置的时间间隔自动打开对话框,但手动点击打开时,线程还会重复打开一次,需要确保不管哪种触发方式,对话框只打开一次。
主对话框触发线程代码
private Thread lockTableThread2 = new Thread(new Runnable() { @Override public void run() { while (cashRegisterOpen) { long currentTime = System.currentTimeMillis() / 1000; if (lockTable){ if (tryOpenCashRegisterDialog(currentTime)) { if (isDialogOpenAtomic.get()) { lastInputTime = System.currentTimeMillis() / 1000; UpdateCurrentUser(); } } } else if (employeeClickTable){ dialog.dispose(); } try { Thread.sleep(1000); } catch (InterruptedException ex) { ex.printStackTrace(); } } } private boolean tryOpenCashRegisterDialog(Long currentTime) { if (!isDialogOpenAtomic.getAndSet(true)) { try { openCashRegisterDialog(currentTime); return true; } finally { isDialogOpenAtomic.set(false); } } return false; } private void openCashRegisterDialog(Long currentTime) { dialog = new ClientAppCashRegisterTablesDialog(null, true, currentTableId, startCashRegisterNumber, invoice.items.isEmpty(), true); if ((currentTime - lastInputTime) > lockTableTime * 2){ dialog.dispose(); ClientAppLogger.GetInstance().LogMessage("Disposing a dialog"); } dialog.addWindowListener(new WindowAdapter() { @Override public void windowClosed(WindowEvent e) { // Reset the flag when the dialog is closed isDialogOpenAtomic.set(false); cashRegisterOpen = true; lockTable = true; employeeClickTable = false; } }); dialog.addWindowListener(new WindowAdapter() { @Override public void windowActivated(WindowEvent e) { if (employeeClickTable){ dialog.setVisible(false); employeeClickTable = false; } } }); dialog.setVisible(true); } });
表格对话框相关代码
if(!thisWindow.isFocused()){ return false; }
我已经试过setModal(true)、dispose()等方法,但问题仍未解决。
问题分析与修复方案
核心问题
当前逻辑存在两处关键漏洞:
tryOpenCashRegisterDialog方法的finally块会在对话框显示后立刻重置isDialogOpenAtomic为false,但模态对话框的setVisible(true)是阻塞调用,这意味着对话框还没关闭,线程就会再次检测到标记为false并尝试打开。- 手动打开对话框的逻辑没有和线程的状态标记同步,导致线程不知道对话框已被手动打开,仍会触发打开操作。
修复步骤
- 统一对话框打开入口
将手动点击和自动触发的逻辑合并到同一个方法,确保状态标记一致。按钮点击事件示例:
buttonOpenTable.addActionListener(e -> { if (!isDialogOpenAtomic.get()) { lastInputTime = System.currentTimeMillis() / 1000; openCashRegisterDialog(lastInputTime); } });
- 修正原子变量控制逻辑
移除tryOpenCashRegisterDialog中的finally块,只在对话框关闭时重置标记:
private boolean tryOpenCashRegisterDialog(Long currentTime) { if (!isDialogOpenAtomic.getAndSet(true)) { openCashRegisterDialog(currentTime); return true; } return false; }
- 增强线程循环的判断条件
在线程循环中增加对话框状态判断,避免重复触发:
@Override public void run() { while (cashRegisterOpen) { long currentTime = System.currentTimeMillis() / 1000; // 增加对话框未打开的判断 if (lockTable && !isDialogOpenAtomic.get()) { if (tryOpenCashRegisterDialog(currentTime)) { lastInputTime = System.currentTimeMillis() / 1000; UpdateCurrentUser(); } } else if (employeeClickTable){ if (dialog != null && dialog.isVisible()) { dialog.dispose(); } } try { Thread.sleep(1000); } catch (InterruptedException ex) { ex.printStackTrace(); } } }
- 手动打开时同步状态标记
在openCashRegisterDialog方法开头设置标记,确保线程能感知到对话框已打开:
private void openCashRegisterDialog(Long currentTime) { isDialogOpenAtomic.set(true); dialog = new ClientAppCashRegisterTablesDialog(null, true, currentTableId, startCashRegisterNumber, invoice.items.isEmpty(), true); if ((currentTime - lastInputTime) > lockTableTime * 2){ dialog.dispose(); ClientAppLogger.GetInstance().LogMessage("Disposing a dialog"); } dialog.addWindowListener(new WindowAdapter() { @Override public void windowClosed(WindowEvent e) { isDialogOpenAtomic.set(false); cashRegisterOpen = true; lockTable = true; employeeClickTable = false; } }); dialog.addWindowListener(new WindowAdapter() { @Override public void windowActivated(WindowEvent e) { if (employeeClickTable){ dialog.setVisible(false); employeeClickTable = false; } } }); dialog.setVisible(true); }
内容的提问来源于stack exchange,提问作者Async Futura
相关产品推荐
相关产品推荐

