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

Swing单次按键触发actionPerformed被双线程执行的原因及解决方法

问题产生原因
  1. 核心触发点是GameLoop类processGameLoop方法的异常处理逻辑:当你按下m键时,EDT(AWT事件调度线程)会触发按键绑定的Action,第一次调用showMenu();showMenu()内部调用GameController.stop(),给运行中的游戏线程发中断信号,游戏线程正在sleep会抛出InterruptedException,而你在catch块里又主动调用了一次showMenu(),这就导致两个线程各调用了一次showMenu()。
  2. 辅助原因:
    • GameLoop的status字段没有加volatile修饰,多线程场景下修改的状态不能保证立即对其他线程可见,可能导致状态判断失效。
    • GameController.stop()方法没有做幂等判断,不管游戏是否已经停止,都会执行重置逻辑,重复调用就会重复执行重置。
    • 游戏线程内直接调用操作Swing组件的showMenu(),违反Swing单线程规则,也会带来额外的线程安全风险。
解决方法
  • 首先删除GameLoop类InterruptedException catch块中的GUIHandler.getInstance().showMenu()调用,主动中断游戏线程属于预期内的操作,不需要额外触发返回菜单逻辑。
  • 给GUIHandler增加是否处于菜单页的状态标记,showMenu()入口处先判断如果已经在菜单页就直接返回,实现方法幂等:
// 在GUIHandler类增加成员变量
private boolean isInMenu = true;

public void startGame() {
    isInMenu = false;
    // 原有逻辑不变
}

public void showMenu()  {
    if (isInMenu) return;
    isInMenu = true;
    // 原有逻辑不变
}
  • 给GameLoop的status字段增加volatile修饰,保证多线程下状态的可见性:
protected volatile String status;
  • 所有操作Swing组件的逻辑确保在EDT线程执行,比如showMenu()里的面板切换逻辑可以套一层SwingUtilities.invokeLater,避免非EDT线程操作UI。
  • 优化GamePanel.reset()方法,去掉里面的initInput()调用,输入绑定只需要在构造函数执行一次即可,不需要每次重置面板重复绑定。

内容的提问来源于stack exchange,提问作者CcmU

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 20:12:00