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

从ActionListener调用JFrame显示异常,求Swing.Timer等解决方案

问题分析与解决方案

你的问题根源在于事件调度线程(EDT)被阻塞了:当你在ActionListener的actionPerformed方法里创建Progress对象时,这个方法本身是在EDT中执行的。而Progress构造函数里的t1.join()会让当前线程(也就是EDT)等待后台线程完成,这直接导致EDT无法处理窗口的绘制请求——直到后台线程跑完,EDT才会一次性刷新UI,所以你看到的是黑色窗口直接跳到进度条填满的状态。

下面给你两种常用的解决思路:


方案一:使用SwingWorker(推荐,适合带后台逻辑的进度更新)

SwingWorker是Swing专门为后台任务设计的工具类,它能在后台线程执行耗时操作,同时安全地在EDT中更新UI,完美适配这种进度条场景。

修改后的Progress类代码如下:

class Progress extends JFrame {
    JProgressBar progressBar;

    Progress() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 300, 300);
        JPanel main = new JPanel();
        add(main);
        main.add(new JLabel("Loading..."));
        
        progressBar = new JProgressBar(0, 100);
        main.add(progressBar);
        
        // 创建SwingWorker处理后台任务
        SwingWorker<Void, Integer> worker = new SwingWorker<>() {
            @Override
            protected Void doInBackground() throws Exception {
                // 后台执行耗时操作,这里模拟进度递增
                for (int pro = 0; pro <= 100; pro++) {
                    Thread.sleep(10);
                    publish(pro); // 发布进度值,会触发process方法
                }
                return null;
            }

            @Override
            protected void process(List<Integer> chunks) {
                // 在EDT中更新UI,chunks是publish传来的进度值列表
                int latestProgress = chunks.get(chunks.size() - 1);
                progressBar.setValue(latestProgress);
            }

            @Override
            protected void done() {
                // 任务完成后的操作(可选)
                JOptionPane.showMessageDialog(Progress.this, "加载完成!");
            }
        };
        
        worker.execute(); // 启动后台任务
        setVisible(true);
    }
}

方案二:使用Swing Timer(适合简单的定时UI更新)

如果你的进度更新逻辑很简单(比如只是模拟递增),javax.swing.Timer更轻量——它的事件处理天然在EDT中执行,不需要手动处理线程安全问题。

修改后的Progress类代码如下:

class Progress extends JFrame {
    int pro = 0;
    JProgressBar progressBar;

    Progress() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 300, 300);
        JPanel main = new JPanel();
        add(main);
        main.add(new JLabel("Loading..."));
        
        progressBar = new JProgressBar(0, 100);
        main.add(progressBar);
        
        // 创建Swing Timer,每隔10ms触发一次事件
        Timer timer = new Timer(10, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (pro <= 100) {
                    progressBar.setValue(pro);
                    pro++;
                } else {
                    ((Timer)e.getSource()).stop(); // 进度完成后停止定时器
                    JOptionPane.showMessageDialog(Progress.this, "加载完成!");
                }
            }
        });
        
        timer.start(); // 启动定时器
        setVisible(true);
    }
}

额外注意点

  • 永远不要在EDT中执行耗时操作或调用Thread.sleep()、join()这类阻塞方法,否则会导致UI卡顿甚至假死。
  • 所有Swing组件的创建和更新操作,都应该在EDT中执行(上述两种方案都自动帮你处理了这一点)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 23:33:10