生产者消费者问题
应用场景:生产者和消费者问题
假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中的产品取走消费
如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,知道仓库中的产品被消费者取走为止
如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,知道仓库中再次放入产品为止
分析
这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件
对于生产者,没有生产产品之前,要通知消费者等待,而生产了产品之后,又需要马上通知消费者消费
对于消费者,在消费之后,要通知生产者已经结束消费,需要生产新的产品以供消费
在生产者消费者问题中,仅有synchronized是不够的
synchronized可组织并发更新同一个共享资源,实现了同步
synchronized不能用来实现不同线程之间的消息传递(通信)
java提供了几个方法解决线程之间的通信问题:
wait() 表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁
wait(long timeout) 指定等待的毫秒数
notify() 唤醒一个处于等待状态的线程
notifyAll() 唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度
注意:均是Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常
解决方式1:管程法
生产者:负责生产数据的模块
消费者:负责处理数据的模块
缓冲区:消费者不能直接使用生产者的数据,他们之间有个缓冲区
生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据
解决方式2:信号灯法
一个标志位,如果为真就等待,如果为假,就通知另一个
管程法
package com.gaoji;
//测试生产者消费者模型-->利用缓冲区解决:管程法
//生产者,消费者,产品,缓冲区
public class TestPC {
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Productor(container).start();
new Consumer(container).start();
}
}
//生产者
class Productor extends Thread{
SynContainer container;
public Productor(SynContainer container) {
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
container.push(new Chicken(i));
System.out.println("生产了"+i+"只鸡");
}
}
}
//消费者
class Consumer extends Thread{
SynContainer container;
public Consumer(SynContainer container) {
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了"+container.pop().id+"只鸡");
}
}
}
//产品
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(Chicken chicken){
//如果容器满了,就需要等待消费者
if (count==chickens.length){
//通知消费者消费,生产等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
chickens[count] = chicken;
count++;
//可以通知消费者消费了
this.notifyAll();
}
//消费者消费产品
public synchronized Chicken pop(){
//判断能否消费
if(count==0){
//等待生产者生产,消费者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果可以消费
count--;
Chicken chicken = chickens[count];
//吃完了,通知生产者生产
this.notifyAll();
return chicken;
}
}
生产了0只鸡
生产了1只鸡
生产了2只鸡
生产了3只鸡
生产了4只鸡
生产了5只鸡
生产了6只鸡
生产了7只鸡
生产了8只鸡
生产了9只鸡
消费了9只鸡
生产了10只鸡
生产了11只鸡
消费了10只鸡
生产了12只鸡
消费了11只鸡
消费了12只鸡
生产了13只鸡
消费了13只鸡
消费了14只鸡
消费了8只鸡
消费了7只鸡
消费了6只鸡
消费了5只鸡
消费了4只鸡
消费了3只鸡
消费了2只鸡
消费了1只鸡
消费了0只鸡
生产了14只鸡
生产了15只鸡
生产了16只鸡
生产了17只鸡
生产了18只鸡
生产了19只鸡
生产了20只鸡
生产了21只鸡
生产了22只鸡
生产了23只鸡
生产了24只鸡
生产了25只鸡
消费了19只鸡
消费了25只鸡
消费了26只鸡
消费了24只鸡
消费了23只鸡
消费了22只鸡
消费了21只鸡
消费了20只鸡
消费了18只鸡
消费了17只鸡
消费了16只鸡
消费了15只鸡
生产了26只鸡
生产了27只鸡
生产了28只鸡
生产了29只鸡
生产了30只鸡
生产了31只鸡
生产了32只鸡
生产了33只鸡
生产了34只鸡
生产了35只鸡
生产了36只鸡
生产了37只鸡
消费了27只鸡
消费了37只鸡
消费了36只鸡
消费了35只鸡
消费了34只鸡
消费了33只鸡
消费了32只鸡
消费了31只鸡
消费了30只鸡
消费了29只鸡
消费了28只鸡
生产了38只鸡
生产了39只鸡
生产了40只鸡
生产了41只鸡
生产了42只鸡
生产了43只鸡
生产了44只鸡
生产了45只鸡
生产了46只鸡
生产了47只鸡
消费了47只鸡
消费了48只鸡
消费了46只鸡
消费了45只鸡
消费了44只鸡
消费了43只鸡
消费了42只鸡
消费了41只鸡
消费了40只鸡
消费了39只鸡
生产了48只鸡
消费了38只鸡
生产了49只鸡
消费了49只鸡
生产了50只鸡
消费了50只鸡
生产了51只鸡
消费了51只鸡
生产了52只鸡
消费了52只鸡
生产了53只鸡
消费了53只鸡
生产了54只鸡
消费了54只鸡
生产了55只鸡
消费了55只鸡
生产了56只鸡
消费了56只鸡
生产了57只鸡
消费了57只鸡
生产了58只鸡
消费了58只鸡
生产了59只鸡
消费了59只鸡