多线程经典问题——生产者消费者精简总结(Java版)

本文详细介绍了Java中使用synchronized和ReentrantLock实现的生产者消费者问题,包括如何避免虚假唤醒,以及如何通过while循环确保同步安全。通过实例展示了如何在Repository类中统一管理和控制生产与消费操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述

有一个缓冲区,生产者往里生产东西,消费者从里消费东西。
缓冲区满时,生产者不能再生产;缓冲区空时,消费者不能再消费。

Java实现关键点

1、实现时声明一个资源类,然后把生产和消费的方法都写里面,生产者和消费者线程里直接调用资源类对象的对应方法即可。不要把生产和消费的方法单独写在生产者类和消费者类中,否则无法控制共享资源的增减。

2、使用lambda表达式来造生产和消费线程,不要显式去造生产者和消费者的类。

3、判断时必须用while不能用if,否则存在虚假唤醒问题。
虚假唤醒(个人理解):多个同类正在wait,notify唤醒了同类。唤醒后的同类有可能会获得锁,则直接从wait开始继续往下执行了,而实际上此时可能并不满足生产或消费的条件,引发错误

4、两套东西,不能混用:

  • synchronized+wait+notify
  • lock+condition.await+condition.signal

synchronized代码实现

package thread;

/**
 * @author cjh
 * @description 完整的生产者消费者实例,synchronized版
 * @create 2021-09-23 16:27
 */
//synchronized同步,线程通信要使用wait和notify()
public class ProduceConsume1 {
    public static void main(String[] args) {
        Repository repository = new Repository();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                Product product = new Product(i);
                repository.produceProduct(product);
            }
        }, "producer1").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                repository.consumeProduct();
            }
        }, "consumer2").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                repository.consumeProduct();
            }
        }, "consumer1").start();



    }
}

class Product {
    int id;

    public Product(int id) {
        this.id = id;
    }
}

class Repository {

    Product[] products = new Product[10];

    private int count = 0;

    public synchronized void produceProduct(Product product) {
        //这里一定要用while,如果用if的话,会存在虚假唤醒问题。本来wait着的同类拿到了锁,继续往下走了,造成数组越界

        //比如当前P1和P2是wait状态,此时C1消费了一个产品后,唤醒了P1和P2,此时P1先拿到锁,又生产了一个产品,
        //此时产品个数为10,P2又拿到锁,接着wait这行往下运行,下面count == 10,数组会越界

        //多个生产者或多个消费者都在wait的时候,notify有可能会唤醒同类

        //除非只有一个生产者和一个消费者的情况下,才能用if
        while (count == 10) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        products[count] = product;
        count++;
        System.out.println(Thread.currentThread().getName() + "已生产完毕,生产的产品id为:" + product.id +
                ",当前资源数: " + count);
        //这里使用notify或者notifyAll没有区别,该继续wait的还是继续wait
        notifyAll();

    }

    public synchronized void consumeProduct() {
        //这里一定要用while,如果用if的话,当唤醒消费者线程时,直接往下走,数组会越界
        while (count == 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //数组索引比长度小1,所以要先--
        count--;
        Product product = products[count];
        System.out.println(Thread.currentThread().getName() + "已消费完毕,消费的产品id为:" + product.id +
                ",当前资源数: " + count);
        notifyAll();

    }
}

lock锁代码实现

package thread;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author cjh
 * @description 完整的生产者消费者实例,lock版
 * @create 2021-10-04 15:50
 */
//lock锁同步,线程通信要使用condition.await()和condition.signalAll()
public class ProduceConsume2 {
    public static void main(String[] args) {
        Repository2 repository = new Repository2();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                Product2 product = new Product2(i);
                repository.produceProduct(product);
            }
        }, "producer1").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                repository.consumeProduct();
            }
        }, "consumer1").start();
    }
}

class Product2 {
    int id;

    public Product2(int id) {
        this.id = id;
    }
}

class Repository2 {

    Lock lock = new ReentrantLock();

    Condition condition = lock.newCondition();

    Product2[] products = new Product2[10];

    private int count = 0;

    public void produceProduct(Product2 product) {
        //try-catch-finally快捷键 ctrl+alt+t
        lock.lock();
        try {
            while (count == 10) {
                condition.await();
            }
            products[count] = product;
            count++;
            System.out.println(Thread.currentThread().getName() + "已生产完毕,生产的产品id为:" + product.id +
                    ",当前资源数: " + count);
            //这里使用notify或者notifyAll没有区别,该继续wait的还是继续wait
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void consumeProduct() {
        lock.lock();
        try {
            while (count == 0) {
                condition.await();
            }
            count--;
            Product2 product = products[count];
            System.out.println(Thread.currentThread().getName() + "已消费完毕,消费的产品id为:" + product.id +
                    ",当前资源数: " + count);
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}


### Python协程的基础概念与使用 Python中的协程是一种用于异步编程的技术,它允许开发者编写非阻塞的代码以处理高并发场景。相比传统的线程或多进程模型,协程更加高效且资源消耗更低。 #### 什么是协程? 协程(Coroutine)是一种用户态下的轻量级线程,它的调度由程序员控制而非操作系统。在Python中,`asyncio`模块是官方支持的协程框架之一,而`greenlet`和`gevent`则是第三方库,分别提供低级别的协程管理和更高层次的任务自动化切换功能[^2]。 #### 如何创建协程? 从Python 3.5开始引入了`async`和`await`关键字,这使得定义和调用协程变得更加直观。下面是一个简单的例子: ```python import asyncio async def greet(name, delay): await asyncio.sleep(delay) # 模拟耗时操作 print(f"Hello {name} after {delay} seconds!") async def main(): task1 = asyncio.create_task(greet("Alice", 2)) task2 = asyncio.create_task(greet("Bob", 1)) await task1 await task2 # 运行事件循环 asyncio.run(main()) ``` 上述代码展示了如何通过`async`声明一个协程函数,并利用`await`暂停当前协程直到另一个协程完成。这里还涉及到了`asyncio.create_task()`方法来并行执行多个任务[^3]。 #### greenlet 和 gevent 的应用 虽然标准库提供了完整的解决方案,但在某些特定需求下可能需要更精细地控制流程或者兼容旧代码。此时可以考虑使用`greenlet`或`gevent`这样的工具包。例如,如果希望手动管理上下文切换,则可以选择前者;而对于那些期望透明化这种复杂性的项目来说,后者无疑更为合适[^4]。 以下是基于`gevent`的一个基本实例: ```python from gevent import monkey; monkey.patch_all() import gevent def worker(message, sleep_time): for i in range(5): print(f"{message}: Iteration {i}") gevent.sleep(sleep_time) if __name__ == "__main__": thread1 = gevent.spawn(worker, "Worker A", 1) thread2 = gevent.spawn(worker, "Worker B", 0.5) threads = [thread1, thread2] gevent.joinall(threads) ``` 在这个脚本里我们模拟了一个生产者消费者模式的工作流,其中两个不同的工作者按照各自的节奏交替打印消息至终端屏幕前。 #### 关于 yield from 的补充说明 当涉及到嵌套生成器的时候,可能会遇到较为复杂的回调链路设计难题。“yield from”的出现极大地简化了这一过程——它可以将内部产生的所有值直接传递给外部迭代器对象而不必逐层返回再转发出去。其背后机制可以通过如下精简本加以诠释[^5]: ```python def subgen(): while True: value = yield if not value: break return 'Done' def delegator(gen): result = yield from gen print(result) sg = subgen() dg = delegator(sg) next(dg) for _ in range(3): dg.send(_) try: dg.send(None) except StopIteration as e: pass finally: del sg,dg ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值