生产者/消费者模式
实际上,很多后台服务程序并发控制的基本原理都可以归纳为生产者/消费者模式。
生产者消费问题是研究多线程程序时绕不开的经典问题之一,它描述是有一块缓冲区作为仓库,消费者则可以从仓库中取走产品。解决生产者/消费者问题的方法可以分为两类:
采用某种机制保护生产者和消费者之间的同步;
生产者和消费者之间建立一个管道。
第一种方式有较高的效率,并且易于实现,代码的可控制性较好,属于常用的模式。
第二种管道缓冲不易控制,被传输数据对象不易于封装,实用性不强。
同步问题的核心在于:如何保证同一资源被多个线程并发访问时的完整性。
准确说应该是"生产者-消费者-仓库"模型,离开了仓库,生产者消费者模型就显得没有说服力了。
对于此模型,应该明确一下几点:
生产者仅仅在仓库未满时候生产,仓满则停止生产。
消费者仅仅在仓库有产品的时候才能消费,仓空则等待。
当消费者发现仓库没产品可消费时候会通知生产者生产。
生产者在生产出可消费产品时候,应该通知等待的消费者取消费。
光看文字可能不太好理解,下面是案例:
1、生产者模块
package com.edu.qwd;
import javax.swing.plaf.synth.SynthSpinnerUI;
//生产者线程要执行的任务
public class Producer implements Runnable {
private Clerk cl;
public Producer(Clerk cl){
this.cl=cl;
}
public void run(){
System.out.println("生产者开始生产产品!");
while(true){
try {
Thread.sleep((int)(Math.random()*10)*100);
} catch (InterruptedException e) {
// TODO 自动生成的catch块
e.printStackTrace();
}
cl.addproduct();//生产产品
}
}
}
2、缓冲电力模块
package com.edu.qwd;
public class Clerk {
private int product=0;//产品默认为0;
//生产者生成出来的产品交给店员
public synchronized void addproduct(){
if(this.product>=20){
try {
wait();//产品已满,请稍等再生产
} catch (InterruptedException e) {
//自动生成的catch块
e.printStackTrace();
}
}else{
product++;
System.out.println("生产者生产产品"+product+"个产品。");
notifyAll();//通知等待区的消费者今天取产品了
}
}
//消费者从店员处取产品
public synchronized void getProduct(){
if(this.product<=0){
try {
wait();//产品没有货了,请稍等再取
} catch (InterruptedException e) {
// TODO 自动生成catch块
e.printStackTrace();
}
}else{
System.out.println("消费者取走了第"+product+"个产品");
product--;
notify();//通知等待区的生产者可以生产产品
}
}
}
3、消费者模块
package com.edu.qwd;
//消费者线程要执行的任务
public class Consumer implements Runnable{
private Clerk cl;
public Consumer(Clerk cl){
this.cl=cl;
}
public void run(){
System.out.println("消费者开始取走产品!");
while(true){
try {
Thread.sleep((int)(Math.random()*10)*100);
} catch (InterruptedException e) {
// TODO 自动生成的catch块
e.printStackTrace();
}
cl.getProduct();//取走产品
}
}
}
4、最后Main方法输出就可以了
package com.edu.qwd;
public class Main {
public static void main(String[] args) {
//自动生成的方法存根
Clerk cl=new Clerk();
Thread prt=new Thread(new Producer(cl));//生产者线程
Thread cot=new Thread(new Consumer(cl));//消费者线程
prt.start();
cot.start();
}
}
使用了生产者/消费者模式之后,生产者和消费者可以是两个独立的并发主体(常见并发类型有进程和线程两种)。生产者把制造出来的数据往缓冲区一丢,就可以去生产下一个数据。基本上不用依赖消费者的处理数据。