多线程球员传球模拟程序异常:执行结果不符预期求助
问题排查与修复方案
核心问题分析
线程私有
phase变量导致阶段不同步
每个Team_A线程都持有独立的phase成员变量,当线程4将自身phase改为Second-phase时,线程1、2、3的phase仍停留在First-phase,导致它们阻塞在第一个循环中。而线程3的phase未更新,会继续执行First-phase逻辑,将ball.nbr_player从3加回4,和线程4形成循环。阶段切换后未唤醒等待线程
线程4完成First-phase切换阶段时,没有调用notifyAll(),其他处于wait状态的线程无法感知阶段变化,持续阻塞。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
相关产品推荐
相关产品推荐

