You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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()等方法,但问题仍未解决。


问题分析与修复方案

核心问题

当前逻辑存在两处关键漏洞:

  1. tryOpenCashRegisterDialog方法的finally块会在对话框显示后立刻重置isDialogOpenAtomic为false,但模态对话框的setVisible(true)是阻塞调用,这意味着对话框还没关闭,线程就会再次检测到标记为false并尝试打开。
  2. 手动打开对话框的逻辑没有和线程的状态标记同步,导致线程不知道对话框已被手动打开,仍会触发打开操作。

修复步骤

  1. 统一对话框打开入口
    将手动点击和自动触发的逻辑合并到同一个方法,确保状态标记一致。按钮点击事件示例:
buttonOpenTable.addActionListener(e -> {
    if (!isDialogOpenAtomic.get()) {
        lastInputTime = System.currentTimeMillis() / 1000;
        openCashRegisterDialog(lastInputTime);
    }
});
  1. 修正原子变量控制逻辑
    移除tryOpenCashRegisterDialog中的finally块,只在对话框关闭时重置标记:
private boolean tryOpenCashRegisterDialog(Long currentTime) {
    if (!isDialogOpenAtomic.getAndSet(true)) {
        openCashRegisterDialog(currentTime);
        return true;
    }
    return false;
}
  1. 增强线程循环的判断条件
    在线程循环中增加对话框状态判断,避免重复触发:
@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();
        }
    }
}
  1. 手动打开时同步状态标记
    在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 21:00:14