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

BlackJack游戏发牌时Thread.sleep失效,卡牌批量显示问题求助

问题分析与解决方案

问题根源

你遇到的核心问题有两个:

  1. UI线程阻塞:Thread.sleep()被直接在Swing的事件调度线程(EDT)中调用,导致整个UI线程被卡住,所有UI更新操作会被积压,直到所有睡眠时长结束后才一次性刷新。
  2. UI更新未触发强制刷新:updateDealerPanel方法仅通过setVisible切换状态,没有通知组件重新布局和绘制,无法确保界面及时更新。

方案1:使用Swing Timer(推荐)

Swing Timer专门用于在EDT中执行延迟任务,不会阻塞UI线程,是处理这类定时UI更新的标准方案:

public static void dealersTurn() {
    Timer timer = new Timer(2000, null);
    timer.addActionListener(e -> {
        DealerHand dealerHand = game.getDealerHand();
        boolean shouldHit = false;

        if (dealerHand.hasAce()) {
            int softTotal = dealerHand.getTotal() + 10;
            if (!dealerHand.didBust() && softTotal < 17) {
                shouldHit = true;
            } else if (!dealerHand.didBust() && softTotal > 21 && dealerHand.getTotal() < 17) {
                shouldHit = true;
            }
        } else {
            if (!dealerHand.didBust() && dealerHand.getTotal() < 17) {
                shouldHit = true;
            }
        }

        if (shouldHit) {
            game.dealCard(dealerHand);
            view.updateDealerPanel();
        } else {
            ((Timer) e.getSource()).stop(); // 停止定时器,结束庄家回合
        }
    });
    timer.setInitialDelay(0); // 第一次发牌无延迟
    timer.start();
}

同时优化updateDealerPanel方法,添加强制刷新逻辑:

public void updateDealerPanel(){
    dealerPanel.removeAll();
    dealerPanel.setLayout(new FlowLayout());
    for(Card card : game.getDealerHand().getCards()){
        JLabel cardPic = new JLabel(new ImageIcon(card.getImg()));
        dealerPanel.add(cardPic);
    }
    JLabel tot = new JLabel("Total: " + game.getDealerHand().getTotalString());
    dealerPanel.add(tot);
    dealerPanel.revalidate(); // 通知布局管理器重新计算布局
    dealerPanel.repaint(); // 强制组件重绘
}

方案2:后台线程+EDT更新UI

如果坚持使用Thread.sleep,必须将庄家逻辑放到后台线程执行,同时确保UI更新操作在EDT中完成:

public static void dealersTurn() {
    new Thread(() -> {
        try {
            DealerHand dealerHand = game.getDealerHand();
            if(dealerHand.hasAce()){
                while(!dealerHand.didBust() && dealerHand.getTotal() +10 < 17){
                    Thread.sleep(2000);
                    game.dealCard(dealerHand);
                    // 在EDT中执行UI更新
                    SwingUtilities.invokeLater(() -> view.updateDealerPanel());
                }
                while(!dealerHand.didBust() && dealerHand.getTotal() + 10 > 21 && dealerHand.getTotal() < 17){
                    Thread.sleep(2000);
                    game.dealCard(dealerHand);
                    SwingUtilities.invokeLater(() -> view.updateDealerPanel());
                }
            }
            else {
                while(!dealerHand.didBust()&& dealerHand.getTotal() < 17){
                    Thread.sleep(2000);
                    game.dealCard(dealerHand);
                    SwingUtilities.invokeLater(() -> view.updateDealerPanel());
                }
            }
        } catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
        }
    }).start();
}

对应的updateDealerPanel方法同样需要添加revalidate()和repaint()(同方案1中的优化版本)。


关键注意点

  • EDT规则:Swing的所有UI创建、更新操作必须在事件调度线程(EDT)中执行,后台线程不能直接操作UI。
  • 避免阻塞EDT:永远不要在EDT中调用Thread.sleep()、网络请求等耗时操作,否则会导致界面卡顿甚至假死。
  • 强制刷新:动态修改组件后,必须调用revalidate()和repaint()来触发界面的重新布局和绘制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 23:35:29