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

使用ActionListener的wait/notify阻塞,无法显示按钮返回Player

问题分析与解决方案

你的代码出现窗口卡住、按钮无法显示的问题,核心原因是违反了Swing的单线程模型:你在主线程(非事件派发线程EDT)中调用了wait(),导致EDT无法执行窗口渲染和事件处理的任务——Swing所有UI相关的操作(包括组件绘制、事件响应)都必须在EDT上完成,主线程阻塞后,EDT被卡死,窗口自然没法正常显示。

另外原代码还有几个可以优化的点:比如通过循环匹配按钮来查找Player的逻辑冗余,静态变量pReturn存在线程安全风险,以及没有在按钮点击后关闭窗口。

修复方案:使用模态对话框替代wait/notify

模态对话框(JDialog)天生就具备「阻塞当前线程直到对话框关闭」的特性,完全适配你的需求,而且符合Swing的线程规范。下面是修改后的代码:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;

public class PlayerSelector {
    // 去掉不必要的静态变量,改用局部变量管理选中的Player
    public static Player playerSelection(ArrayList<Player> players, String title) {
        final Player[] selectedPlayer = new Player[1]; // 用数组保存选中结果,方便在匿名类中修改
        JDialog dialog = new JDialog((Frame) null, title, true); // 创建模态对话框
        JPanel panel = new JPanel(new GridLayout(-1, 1, 5, 5));
        
        for (Player player : players) {
            JButton btn = new JButton(player.getName());
            panel.add(btn);
            btn.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    selectedPlayer[0] = player; // 直接绑定当前Player,无需循环查找
                    dialog.dispose(); // 点击后关闭对话框
                }
            });
        }
        
        dialog.add(panel);
        dialog.pack();
        dialog.setLocationRelativeTo(null); // 居中显示
        dialog.setVisible(true); // 显示模态对话框,当前线程会阻塞直到对话框关闭
        
        return selectedPlayer[0]; // 返回选中的Player,未选中则返回null
    }

    // 示例Player类
    static class Player {
        private String name;
        public Player(String name) { this.name = name; }
        public String getName() { return name; }
    }
}

关键改进点说明

  • 使用模态JDialog:setVisible(true)会阻塞当前线程,直到对话框被关闭,完全替代了你原来的wait/notify逻辑,且符合Swing线程规范。
  • 直接绑定Player对象:在创建按钮时就捕获对应的Player,避免了原代码中循环匹配按钮的冗余逻辑,代码更简洁高效。
  • 移除静态变量:用局部数组selectedPlayer保存选中结果(因为匿名内部类只能访问final局部变量,数组是引用类型可以修改内部值),避免了静态变量带来的线程安全问题。
  • 自动关闭窗口:点击按钮后调用dialog.dispose()关闭对话框,同时解除线程阻塞。

原代码的核心问题复盘

  1. 线程阻塞导致EDT无法工作:你在主线程中调用o.wait(),主线程被挂起,但Swing的EDT需要主线程释放资源才能完成窗口绘制,导致窗口一直处于未渲染状态。
  2. 静态变量的线程风险:pReturn是静态变量,如果多个地方调用playerSelection方法,会出现结果被覆盖的问题。
  3. 未关闭窗口:原代码中点击按钮后只调用了o.notify(),没有关闭窗口,用户体验不好。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:28:07