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

多线程球员传球模拟程序异常:执行结果不符预期求助

问题排查与修复方案

核心问题分析

  1. 线程私有phase变量导致阶段不同步
    每个Team_A线程都持有独立的phase成员变量,当线程4将自身phase改为Second-phase时,线程1、2、3的phase仍停留在First-phase,导致它们阻塞在第一个循环中。而线程3的phase未更新,会继续执行First-phase逻辑,将ball.nbr_player从3加回4,和线程4形成循环。

  2. 阶段切换后未唤醒等待线程
    线程4完成First-phase切换阶段时,没有调用notifyAll(),其他处于wait状态的线程无法感知阶段变化,持续阻塞。

  3. Second-phase逻辑缺失关键操作
    原代码中Second-phase未输出当前球员编号,且切换回First-phase时未唤醒线程,后续流程无法正常触发。


修复后的代码

1. 共享状态的Ball类

将phase改为共享变量,确保所有线程访问同一状态:

package Ball_Game;

public class Ball{
    int nbr_player = 1;
    // 共享阶段变量,所有线程共用
    public String phase = "First-phase";

    public synchronized void throw_ball() {
        System.out.println(Thread.currentThread().getName() + " :throws ball");
    }

    public synchronized void throw_ball_back() {
        System.out.println(Thread.currentThread().getName() + " :throws ball back");
    }

    public synchronized void increment() {
        nbr_player++;
    }

    public synchronized void decrement() {
        nbr_player--;
    }
}

2. 修正后的Team_A线程类

移除私有phase,基于共享状态判断流程,添加必要的唤醒操作:

package Ball_Game;

public class Team_A extends Thread {
    int ID;
    Ball ball;

    public Team_A(int ID, Ball ball) {
        this.ball = ball;
        this.ID = ID;
        // 设置线程名称,方便日志区分
        this.setName("Player-" + ID);
    }

    @Override 
    public void run() {
        synchronized (ball) {
            while (true) {
                // 循环检查第一阶段条件,防止虚假唤醒
                while ("First-phase".equals(ball.phase)) { 
                    if (ID == ball.nbr_player) {
                        ball.throw_ball();
                        
                        if (ID != 4) {
                            ball.increment();
                            System.out.println("Next receiver: " + ball.nbr_player);
                            ball.notifyAll();
                        } else {
                            ball.phase = "Second-phase";
                            System.out.println("Switched to: " + ball.phase);
                            ball.notifyAll(); // 唤醒所有线程,通知阶段切换
                        }
                    } else {
                        try {
                            ball.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }    
                } 

                // 循环检查第二阶段条件
                while ("Second-phase".equals(ball.phase)) { 
                    if (ID == ball.nbr_player) {
                        ball.throw_ball_back();
                        if (ball.nbr_player > 1) {
                            ball.decrement();
                            System.out.println("Next receiver: " + ball.nbr_player);
                            ball.notifyAll();
                        } else {
                            ball.phase = "First-phase";
                            System.out.println("Switched to: " + ball.phase);
                            ball.notifyAll(); // 唤醒所有线程,触发新一轮循环
                        }
                    } else {
                        try {
                            ball.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }    
                }
            }
        }
    }
}

3. 测试主类

package Ball_Game;

public class Main {
    public static void main(String[] args) {
        Ball ball = new Ball();
        for (int i = 1; i <= 4; i++) {
            new Team_A(i, ball).start();
        }
    }
}

修复关键点

  • 共享状态同步:将phase移至Ball类,确保所有线程感知统一的阶段状态。
  • 强制唤醒机制:阶段切换后调用notifyAll(),避免线程永久阻塞。
  • 循环条件检查:用内层循环包裹wait()逻辑,防止虚假唤醒导致的流程异常。
  • 日志优化:设置线程名称并输出下一个接球球员,便于调试和验证流程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 08:25:19