Java多线程三吸烟者(Three Smokers)模型代码运行异常求解
三吸烟者问题排查与修复
核心问题原因
- 物品状态判断无同步保护:吸烟者线程中检查物品是否匹配、打印取物日志的逻辑没有锁约束,多线程可同时读取Agent的共享状态,导致多个线程同时判定自己符合取物条件,连续输出取物日志,仅在调用
smoke()方法时才排队执行,这是你看到多个取物日志同时出现的直接原因。 - 角色逻辑错位:
putTable()是Agent负责放置物品的专属逻辑,不应由每个吸烟者线程循环调用。当前三个吸烟者线程都在并发修改Agent的物品状态,直接导致共享状态混乱。 putTable()方法语法错误:switch语句未加break,匹配到对应case后会继续执行后续所有case的逻辑,大概率会把三个物品的布尔值全部设为true,导致三个吸烟者同时满足取物条件。- 共享状态操作非线程安全:
putTable()方法无同步控制,多线程并发修改物品状态时会出现原子性、可见性问题。
修复方案
1. 角色逻辑拆分
新增Agent独立线程,仅由Agent负责循环放置物品,吸烟者线程仅负责判断取物、吸烟逻辑。
2. 临界区加锁
将「检查物品是否匹配→打印取物日志→调用smoke方法」的整个逻辑加synchronized锁,保证同一时间仅一个线程能执行该逻辑。
3. 修复putTable()语法问题
给switch分支加break,优化放物品逻辑保证每次仅放置两种。
4. 共享状态操作同步
所有修改Agent物品状态的方法都加同步控制,保证状态可见性。
关键修复代码示例
修复后的Agent类
public class Agent extends Thread { boolean paper; boolean tobacco; boolean lighters; // 同步方法:放物品,每次只放两种 public synchronized void putTable() { // 先清空桌面 paper = tobacco = lighters = false; Random random = new Random(); int skip = random.nextInt(3); // 随机跳过一种不放,剩下两种都放 if (skip != 0) paper = true; if (skip != 1) tobacco = true; if (skip != 2) lighters = true; System.out.println("Agent put 2 items on table"); notifyAll(); // 通知所有吸烟者可以检查物品 } // 同步方法:吸烟逻辑 public synchronized void smoke(Pusac smoker) { System.out.println("Smoker with ID " + smoker.id + " is smoking."); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } // 清空桌面物品 paper = tobacco = lighters = false; System.out.println("Smoker with ID " + smoker.id + " finished smoking, clear table."); notifyAll(); // 通知Agent可以放新的物品 } @Override public void run() { while (true) { putTable(); try { wait(); // 放完物品等待吸烟者吸烟结束 } catch (InterruptedException e) { e.printStackTrace(); } } } }
修复后的吸烟者(Pusac)类
public class Pusac extends Thread { int id; Agent a; String owns; public Pusac(int id, Agent a, String owns) { this.id = id; this.a = a; this.owns = owns; } @Override public void run() { while (true) { synchronized (a) { // 等待Agent放完物品,或者自己不符合条件时等待 while (!checkMatch()) { try { a.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 符合条件,取物 if (this.owns.equals("Paper")) { System.out.println("Smoker with ID " + this.id + " took tobacco and lighters."); } else if (this.owns.equals("Tobacco")) { System.out.println("Smoker with ID " + this.id + " took lighters and paper."); } else if (this.owns.equals("Lighters")) { System.out.println("Smoker with ID " + this.id + " took paper and tobacco."); } a.smoke(this); } } } // 检查当前物品是否匹配自己的需求 private boolean checkMatch() { if (this.owns.equals("Paper")) return a.tobacco && a.lighters; if (this.owns.equals("Tobacco")) return a.lighters && a.paper; if (this.owns.equals("Lighters")) return a.paper && a.tobacco; return false; } }
修复后的Main类
public class Main { public static void main(String[] args) { Agent a = new Agent(); Pusac p1 = new Pusac(0,a,"Paper"); Pusac p2 = new Pusac(1,a,"Tobacco"); Pusac p3 = new Pusac(2,a,"Lighters"); a.start(); p1.start(); p2.start(); p3.start(); } }
内容的提问来源于stack exchange,提问作者CupidONO
相关产品推荐
相关产品推荐

