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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 15:06:07