生产者-消费者问题

使用synchronized实现生产消费

虚假等待

虚假唤醒意味着线程在没有满足 wait() 条件的情况下被唤醒。由于 wait() 可能会由于多种原因(如操作系统调度等)发生虚假唤醒,线程被唤醒后可能发现它原本等待的条件并未改变

示例分析:

scss 复制代码
public synchronized void produce() throws InterruptedException {
    if (queue.size() == 1) {
        wait();  // 如果缓冲区满了,生产者等待
    }
    queue.add(1);  // 生产产品
    notify();  // 唤醒消费者
}

public synchronized void consume() throws InterruptedException {
    if (queue.isEmpty()) {
        wait();  // 如果缓冲区空了,消费者等待
    }
    queue.remove(0);  // 消费产品
    notify();  // 唤醒生产者
}

这里使用了if来判断是否(满/空);

假设一下步骤发生:

1.生产者线程add(1),然后唤醒其他线程;

2.由于某些原因(比如操作系统调度、虚假唤醒等),消费线程 被唤醒,即使缓冲区仍然为空, 它依然开始执行。

3.消费线程 执行判断queue.isEmpty(),如果他被虚假唤醒,判断结果为true,然后线程会继续进入消费逻辑,尝试从一个空的缓冲区取数据。

反制生产者亦是。

因为 if只能判断一次,如果被虚假唤醒消费线程后,就失效了。 使用 while 循环代替 if 可以解决这个问题,因为 while 在每次被唤醒时重新检查条件 ,确保条件符合才继续执行

scss 复制代码
public synchronized void produce() throws InterruptedException {
    wgile (queue.size() == 1) {
        wait();  // 如果缓冲区满了,生产者等待
    }
    queue.add(1);  // 生产产品
    notify();  // 唤醒消费者
}

public synchronized void consume() throws InterruptedException {
    wgile (queue.isEmpty()) {
        wait();  // 如果缓冲区空了,消费者等待
    }
    queue.remove(0);  // 消费产品
    notify();  // 唤醒生产者
}

Lock版的生产者消费问题

synchronized的对比,三件套。

使用LockConditon实现的生产消费,下边这个代码本质和synchronized类似

csharp 复制代码
public class ProducerConsumer {


    public static void main(String[] args) {
        Task task = new Task();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                task.inSale();
            }
        }, "生产者1").start();


        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                task.inSale();
            }
        }, "生产者2").start();



        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                task.deSale();
            }
        }, "消费者1").start();


        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                task.deSale();
            }
        }, "消费者2").start();

    }

}

class Task {
    public Integer count = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();

    public void inSale() {
        lock.lock();
        try {
            while (count != 0) { 
                condition.await();
            }
            count++;
            System.out.println(Thread.currentThread().getName() + "生产了一个产品,目前产品数量为:" + count);
            condition.signalAll(); // 唤醒所有等待线程
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }

    public void deSale() {
        lock.lock();
        try {
            while (count == 0) { 
                condition.await();
            }
            count--;
            System.out.println(Thread.currentThread().getName() + "消费了一个产品,目前产品数量为:" + count);
            condition.signalAll(); // 唤醒所有等待线程
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }
}

但是你会发现他没有按照一定顺序生产消费

Condition实现精准通知和唤醒线程

使用Condition可以精准唤醒你想要实现的线程 例如:A->C->B->D

csharp 复制代码
public class ProducerConsumer {


    public static void main(String[] args) {
        Task task = new Task();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                task.printA();
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                task.printB();
            }
        }, "B").start();
        
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                task.printC();
            }
        }, "C").start();
        
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                task.printD();
            }
        }, "D").start();
    }

}

class Task {
    Lock lock = new ReentrantLock();
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();
    Condition condition3 = lock.newCondition();
    Condition condition4 = lock.newCondition();
    //1=》A 2=》B 3=》C 4=》D
    private int number = 1;

    public void printA() {
        lock.lock();
        try {
            while (number != 1) {
                condition1.await();
            }
            number = 3;
            System.out.println(Thread.currentThread().getName() + "生产了一个产品,唤醒C:" + number);
            condition3.signal(); // 唤醒C线程
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }
    public void printB() {
        lock.lock();
        try {
            while (number != 2) {
                condition2.await();
            }
            number = 4;
            System.out.println(Thread.currentThread().getName() + "生产了一个产品,唤醒D:" + number);
            condition4.signal(); // 唤醒D线程
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }

    public void printC() {
        lock.lock();
        try {
            while (number != 3) {
                condition3.await();
            }
            number = 2;
            System.out.println(Thread.currentThread().getName() + "生产了一个产品,唤醒B:" + number);
            condition2.signal(); // 唤醒A线程
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }
    public void printD() {
        lock.lock();
        try {
            while (number != 4) {
                condition4.await();
            }
            number = 1;
            System.out.println(Thread.currentThread().getName() + "生产了一个产品,唤醒A:" + number);
            condition1.signal(); // 唤醒B线程
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }
}
相关推荐
zb200641204 小时前
CVE-2024-38819:Spring 框架路径遍历 PoC 漏洞复现
java·后端·spring
uzong4 小时前
AI Agent 是什么,如何理解它,未来挑战和思考
人工智能·后端·架构
追逐时光者4 小时前
DotNetGuide突破了10K + Star,一份全面且免费的C#/.NET/.NET Core学习、工作、面试指南知识库!
后端·.net
yuweiade4 小时前
springboot和springframework版本依赖关系
java·spring boot·后端
ywf12154 小时前
springboot设置多环境配置文件
java·spring boot·后端
小马爱打代码5 小时前
SpringBoot + 消息生产链路追踪 + 耗时分析:从创建到发送,全链路性能可视化
java·spring boot·后端
小码哥_常5 小时前
MyBatis批量插入:从5分钟到3秒的逆袭之路
后端
烛之武6 小时前
SpringBoot基础
java·spring boot·后端
橙序员小站7 小时前
Harness Engineering:从 OpenClaw 看 AI 助理的基础设施建设
后端·aigc·openai
小陈工7 小时前
2026年3月28日技术资讯洞察:5G-A边缘计算落地、低延迟AI推理革命与工业智造新范式
开发语言·人工智能·后端·python·5g·安全·边缘计算