代码作者链接:https://blog.csdn.net/yjsz2010/article/details/82729562
本文是对作者代码的分析
线程之并发协作:生产者消费者简单模型
对于多线程程序来说,生产者和消费者模型都是最经典的。
对于此模型,应该注意 :
- 生产者仅仅在仓储未满时候生产,仓满则停止生产。
- 消费者仅仅在仓储有产品时候才能消费,仓空则等待。
- 当消费者发现仓储没产品可消费时候会通知生产者生产。
- 生产者在生产出可消费产品时候,应该通知等待的消费者去消费。
需要结合wait、notify、notifyAll来实现
代码分析
其实消费者和生产者离开了仓库也就不行了,这个模型把对应的控制都放到了仓库类中
主要思路,分别定义一个仓库对象(godown)、多个生产者(Producer )、多个消费者(Godown )。通过厂库对于其他线程的控制来实现对应的场景。
生产者的类:
class Producer extends Thread{
private int neednum;//生产产品的数量
private Godown godown;//仓库
Producer(int neednum, Godown godown) {
this.neednum = neednum;
this.godown = godown;
}
public void run(){
//生产指定数量的产品
godown.produce(neednum);
}
}
消费者的类:
class Consumer extends Thread{
private int neednum;//消费产品的数量
private Godown godown;//仓库
Consumer(int neednum, Godown godown) {
this.neednum = neednum;
this.godown = godown;
}
public void run(){
//消费指定数量的产品
godown.consume(neednum);
}
}
由以上代码可知,Producer和Consumer都继承了线程的Thread类,并复写了其中的run方法。在run方法中分别调用了Godown的 godown.produce(neednum); 和godown.consume(neednum);
他们只负责对应的生产调用和消费。而核心的线程控制模块放到了Godown中。
本代码中俩个线程交互函数的介绍:
这些方法必须从同步环境的内部来调用。线程不能调用对象上等待或通知的方法,除非它拥有那个对象的锁。
wait()、notify()、notifyAll()都是Object的实例方法。与每个对象具有锁一样,每个对象可以有一个线程列表,他们等待来自该信号(通知)。线程通过执行对象上的wait()方法获得这个等待列表。从那时候起,它不再执行任何其他指令,直到调用对象的notify()方法为止。**如果多个线程在同一个对象上等待,则将只选择一个线程(不保证以何种顺序)**继续执行。如果没有线程等待,则不采取任何特殊操作。
如果一个线程在对象上获得一个锁,就没有任何其他线程可以进入(该对象的)类中的任何一个同步方法(睡眠不释放锁)
wait()的作用是让当前线程进入等待状态,同时,wait()也会让当前线程释放它所持有的锁。
所以该代码是将对应的线程放入仓库对象的等待列表中,释放该对象的锁,直到notifyall()所有的等待线程。
- wait():
- void wait(longtimeout)——导致当前的线程等待,直到其他线程调用此对象的 notify()方法或 notifyAll()方法,或者超过指定的时间量。
- void wait(longtimeout, int nanos)——导致当前的线程等待,直到其他线程调用此对象的 notify()方法或 notifyAll()方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量。
- void wait()——导致当前的线程等待,直到其他线程调用此对象的 notify()方法或 notifyAll()方法。
-
notifyall()
void notifyAll()——唤醒在此对象监视器上等待的所有线程
仓库类:
class Godown{
public static final int max_size=100;//最大库存量
public int curnum;//当前库存量
Godown() {
}
Godown(int curnum){
this.curnum=curnum;
}
/** 生产指定数量的产品 **/
public synchronized void produce(int neednum){
//测试是否需要生产
while(neednum+curnum>max_size){
System.out.println("要生产的产品数量" + neednum +"超过剩余库存量" + (max_size - curnum) +",暂时不能执行生产任务!");
try {
//当前的生产线程等待
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//满足生产条件,则进行生产,这里简单的更改当前库存量
curnum+=neednum;
System.out.println("已经生产了"+neednum+"个产品,现仓储量为"+curnum);
//唤醒在此对象监视器上等待的所有线程
notifyAll();
}
/**
* 消费指定数量的产品
*/
public synchronized void consume(int neednum){
//测试是否可以消费
while(curnum<neednum){
try {
//当前的生产线程等待
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//满足消费条件,则进行消费,这里简单的更改当前库存
curnum-=neednum;
System.out.println("已经消费了" + neednum +"个产品,现仓储量为" + curnum);
//唤醒在此对象监视器上等待的所有线程
notifyAll();
}
}
仓库类中一共俩个同步方法
public synchronized void produce(int neednum)
public synchronized void consume(int neednum)
都通过while()条件判断,如果不符合条件,就让对应的线程进行等待,而又因为produce,consume方法始终会有一个在执行,这就使得该程序不会出现死锁。