生产者消费者模型应该是计算机经常涉及到的,我在上计算机组成原理、操作系统课时,都有讲到过,而此模型在编程中也是会经常涉及到。生产者负责生产数据,消费者负责消耗数据,如果我们直接让消费者去调用生产者里面的方法去消耗数据的话,要是某一天,消费者的代码发生变化,生产者可能也会受到影响。简单来说,生产者消费者之间应该是通过一个中间缓冲区去相互使用,这样它们之间的依赖关系就没有那么强烈,也达到了松耦合的目的。
现在有个需求,生产者生产手机,消费者消费手机,生产两种类型的手机,且要交替生产,下面看看代码:
/**
* 建立手机实体类,设置属性有品牌、价格
* @author littledyf
*
*/
public class Phone {
private String brand;
private double price;
/*设置库存,如果没有,store为false,如果有store为true;
* 没有的时候,由生产者生产,消费者不能消费;
* 有的时候,由消费者消费。生产者不能生产。
*/
private boolean store = false;
public boolean isStore() {
return store;
}
public void setStore(boolean store) {
this.store = store;
}
public Phone() {
}
public Phone(String brand, double price) {
this.brand = brand;
this.price = price;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Phone [brand=" + brand + ", price=" + price + "]";
}
}
public class Producer extends Thread{
private Phone phone;
public Producer(Phone phone) {
// TODO Auto-generated constructor stub
this.phone = phone;
}
@Override
public void run() {
// TODO Auto-generated method stub
boolean bool = true;
while(true){
//可以直接用phone作为锁对象,因为phone只有一个对象
synchronized (phone) {
//当有库存的时候,即store为true的时候,等待
if(phone.isStore() == true){
try {
phone.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//如果bool为true,生产HTC,否则生产中兴
if(bool == true){
phone.setBrand("HTC");
phone.setPrice(200);
}else{
phone.setBrand("中兴");
phone.setPrice(100);
}
//每生产一次就置反,为了让另外一个生产
bool = !bool;
//生产了之后将库存置为true,这时不能生产,只能消费
phone.setStore(true);
//当生产了之后,唤醒消费者线程
phone.notify();
}
}
}
}
public class Consumer extends Thread{
private Phone phone;
public Consumer(Phone phone) {
// TODO Auto-generated constructor stub
this.phone = phone;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
synchronized (phone) {
//如果没有库存,此线程等待
if(phone.isStore() == false){
try {
phone.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(phone.getBrand() + "---" + phone.getPrice());
//消费了之后,将库存设置为false
phone.setStore(false);
//消费了之后,唤醒生产线程,这里的phone为监视器
phone.notify();
}
}
}
}
/**
* 生产者消费者测试
* 需求:生产者生产两种型号的手机,且要求交替生产,消费者去进行消费
* @author littledyf
*
*/
public class TestPC {
public static void main(String[] args) {
//创建实体类对象
Phone phone = new Phone();
//创建任务,多个线程执行多个任务,但任务又是相关联的
Producer p = new Producer(phone);
Consumer c = new Consumer(phone);
p.start();
c.start();
}
}
分析:看到需求之后,我们先分析从哪里下手,竟然是一个生产手机,一个消费手机,那么,是不是需要先生产才能消费?要生产手机,就应该先有手机这个类,那我们先创建手机类。创建好手机类后,再去创建生产者,生产者继承Thread,生产是一个线程嘛。生产手机,那么久把手机类phone传入生产者,生产者重写run方法。这里直接while(true)不停生产,我们需要在没有库存的时候再去生产,所以,我们需要在手机类里设置库存,刚开始store为false,就是当store为false时才生产,true的时候不生产,开始等待消费者消费。然后生产手机,需要交替生产,那么我们直接用一个boolean值来进行交替,当bool为true的时候生产一种机型,生产了之后bool置反,当bool为false生产另外一种,生产完又置反。这时生产好了,我们就需要唤醒消费者线程。值得注意的是,为了不让两者争抢资源发生不好的事情,我们需要加锁,而锁对象直接可以使用phone对象。我们不是让在有库存和没库存的时候去等待另外一个线程运行嘛,所以,我们需要等待和唤醒,而等待和唤醒我们需要一个监视器,而监视器用哪个对象?简单一点,锁对象是谁,就用谁作为监视器。而关于监视器,可以再去看看相关的文章,我们这里主要讲解生产者消费者。其实在写代码的时候,我们不需要一次性把一个类写完,而是分析先需要写哪个,写完再写下一个,然后看下一个需要什么,我们再去写需要的东西,给我的感觉就像是一个切面的感觉。就是很多类或者对象之间不是垂直的关系,而是一种面向切面的那种关系。
接下来我们看看使用仓储来实现生产者消费者案例:
**
* 生产者消费者仓储模型
* @author littledyf
*
*/
public class Store {
private int max = 20;//最大存储量
private int sum = 0;//产品数量
//入库
public void push() {
// TODO Auto-generated method stub
synchronized (this) {
//如果产品数量大于等于最大存储量,等待
if(sum >= max){
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//商品入库之后加1
sum++;
System.out.println("商品入库:" + sum);
//唤醒出库
this.notify();
}
}
//出库
public void pop() {
// TODO Auto-generated method stub
synchronized (this) {
//如果商品存量为0,等待
if(sum == 0){
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//商品出库减1
sum--;
System.out.println("商品出库,剩余商品:" + sum);
//唤醒入库
this.notify();
}
}
}
public class Producer extends Thread{
private Store store;
public Producer(Store store) {
this.store = store;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
//生产者调用仓储模型里的入库
store.push();
}
}
}
public class Consumer extends Thread{
private Store store;
public Consumer(Store store) {
this.store = store;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
//消费者调用仓储模型里的出库
store.pop();
}
}
}
public class TestPCS {
public static void main(String[] args) {
//建立一个仓储模型的对象
Store store = new Store();
//让生产者和消费者分别去仓储模型里调用该使用的方法
Producer p = new Producer(store);
Consumer c = new Consumer(store);
p.start();
c.start();
}
}
生产者消费者的仓储模型,其实就是生产者消费者不做具体的方法,具体方法放在一个仓库,即store里面去实现,生产者消费者只需要去调用仓库里面的方法即可。
之后在编程的时候,只要遇到多个线程去执行多个任务,而多个任务又是相互关联的,我们就可以使用生产者消费者模型直接去套。