二十五、生产者消费者问题
线程通信:
- 应用场景:生产者和消费者问题
- 假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库﹐消费者将仓库中产品取走消费
- 如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止
- 如果仓库中放有产品,则消费者可以将产品取走消费﹐否则停止消费并等待,直到仓库中再次放入产品为止
- 分析:这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件
-
对于生产者,没有生产产品之前,要通知消费者等待.而生产了产品之后﹐又需要马上通知消费者消费
-
对于消费者﹐在消费之后,要通知生产者已经结束消费
-
需要生产新的产品以供消费.在生产者消费者问题中
-
仅有synchronized是不够的
-
synchronized可阻止并发更新同一个共享资源,实现了同步
-
synchronized不能用来实现不同线程之间的消息传递(通信)
-
-
- Java提供了几个方法解决线程之间的通信问题。注意:均是Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常lllegalMonitorStateException
方法名 | 作用 |
wait() | 表示线程一直等待﹐直到其他线程通知,与sleep不同,会释放锁 |
wait(long timeout) | 指定等待的毫秒数 |
notify() | 唤醒一个处于等待状态的线程 |
notifyAll() | 唤醒同一个对象上所有调用wait()方法的线程﹐优先级别高的线程优先调度 |
- 并发协作模型“生产者/消费者模式”:生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据
- 生产者:负责生产数据的模块(可能是方法﹐对象﹐线程﹐进程);
- 消费者:负责处理数据的模块(可能是方法﹐对象,线程,进程);
- 缓冲区:消费者不能直接使用生产者的数据﹐他们之间有个“缓冲区;
-
-
- 解决方式1:管程法(缓冲区)
-
public class ProducersAndConsumers {
public static void main(String[] args) {
SynContainer synContainer = new SynContainer();
Producers p = new Producers(synContainer);
Consumers c = new Consumers(synContainer);
new Thread(p).start();
new Thread(c).start();
}
}
//生产者
class Producers implements Runnable {
SynContainer synContainer;
public Producers(SynContainer synContainer) {
this.synContainer = synContainer;
}
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
try {
synContainer.push(i);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
//消费者
class Consumers implements Runnable {
SynContainer synContainer;
public Consumers(SynContainer synContainer) {
this.synContainer = synContainer;
}
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
try {
synContainer.pop(i);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
//产品
class Chicken {
int id;
public Chicken(int id) {
this.id = id;
}
}
//缓冲区
class SynContainer {
Chicken[] chickens = new Chicken[10];
int count = 0;
//生产
public synchronized void push(int i) throws InterruptedException {
while (chickens.length == count) {
this.wait();
}
Chicken chicken = new Chicken(i);
chickens[count] = chicken;
count++;
System.out.println("生产第" + i + "只鸡");
this.notifyAll();
}
//消费
public synchronized void pop(int i) throws InterruptedException {
while (count == 0) {
this.wait();
}
//模拟延迟消费
Thread.sleep(100);
count--;
System.out.println("消费第" + i + "只鸡");
Chicken chicken = chickens[count];
this.notifyAll();
}
}
-
-
- 解决方式2:信号灯法(标志位)
-
public class ProducersAndConsumers2 {
public static void main(String[] args) {
Product product = new Product();
Producers2 p = new Producers2(product);
Consumers2 c = new Consumers2(product);
new Thread(p).start();
new Thread(c).start();
}
}
//生产者
class Producers2 implements Runnable {
Product product;
public Producers2(Product product) {
this.product = product;
}
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
try {
product.push(i);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
//消费者
class Consumers2 implements Runnable {
Product product;
public Consumers2(Product product) {
this.product = product;
}
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
try {
product.pop(i);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
//缓冲区
class Product {
//产品
int id;
//标志位
boolean flag = false;
//生产
public synchronized void push(int id) throws InterruptedException {
while (flag) {
this.wait();
}
this.flag = !this.flag;
System.out.println("生产了" + id + "号");
this.notifyAll();
}
//消费
public synchronized void pop(int id) throws InterruptedException {
while (!flag) {
this.wait();
}
this.flag = !this.flag;
System.out.println("消费了" + id + "号");
this.notifyAll();
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!