生产者—消费者问题:多线程访问的一个经典案例,即按面向对象的思想来分析问题的话,这个问题涉及四个对象,生产者Procedure、消费者Consumer、产品Product、仓库WareHouse。下面以一个简单的例子来进行实现。
一、产品Product
每个产品有一个属性id,这个id值由生产者赋予。即生产者调用产品的构造方法来设置该属性id。
class Product { int id=0; Product(int id){ this.id=id;} public String toString(){ return "Product: "+this.id; } }
二、仓库WareHouse
1、仓库有两个属性:库存num、容量,库存用int表示,初始化为0;容量用数组的长度表示。库存即该产品数组中的前num个产品。
2、两个方法:增加库存add和减少库存del。在add中将传入的产品参数放入库存数组,将库存量加一。减少库存del中将库存量num减一、并返回消费产品。
3、在add和del方法中,执行前后应该检查库存,若执行add方法,当库存为库存的容量时,使得当前线程wait,该线程(为生产者对象)放弃锁。直到当消费者线程执行del使得库存量小于仓库容量时,唤醒当前进程,重新获得锁,继续执行生产add。若执行del,当库存为0时,使得当前进程wait,释放锁,直到生产者进程获取锁之后执行add后库存大于0,此时唤醒进程重新获得锁,执行消费del。
4、在add和del方法中,可能出现多个生产者和多个消费者同时访问库存并执行add和del,所以这两个方法都应设置为同步方法。
class WareHouse { int num=0; Product[] list=new Product[10]; public synchronized void add(Product p){ while(num==list.length-1){ try{this.wait(); }catch(Exception e){ e.printStackTrace(); } } this.notifyAll(); list[num]=p; num++; } public synchronized Product del(){ while(num==0){ try{this.wait(); }catch(Exception e){ e.printStackTrace(); } } this.notifyAll(); num--; return list[num]; } }
三、生产者Procedure
生产者构造方法传入一个WareHouse参数生成生产者。每个Produre都是一个进程,所以实现Runnable接口和相应的run方法,在run方法中,每个Procedure可以生产10件产品。
class Procedure implements Runnable { WareHouse wh=null; Procedure(WareHouse wh){ this.wh = wh;} public void run(){ for(int i=0;i<10;i++){ Product pp =new Product(i); wh.add(pp); System.out.println(Thread.currentThread()+"生产了:"+pp); } } }
四、消费者Consumer
消费者由传入一个WareHouse参数构造。同理,每个Consumer也实现了Runnable接口和run方法,在run方法中,设定每个消费者可以消费5件产品。
class Consumer implements Runnable { WareHouse wh=null; Consumer(WareHouse wh){ this.wh = wh;} public void run(){ for(int i=0;i<5;i++){ Product ppp= wh.del(); System.out.println(Thread.currentThread()+"消费了:"+ppp); } } }
五、测试类:在main方法中首先建立一个WareHouse对象,然后利用WareHouse对象新建Procedure对象和Consumer对象各俩,最后利用Procedure和Consumer对象新建四个进程,然后start这四个进程。
public class P_C { public static void main(String[] args) { WareHouse wh =new WareHouse(); Procedure p1=new Procedure(wh); Procedure p2=new Procedure(wh); Consumer m1 = new Consumer(wh); Consumer m2 =new Consumer(wh); Thread t1 =new Thread(p1); Thread t2 =new Thread(p2); Thread t3 =new Thread(m1); Thread t4 =new Thread(m2); t1.start(); t2.start(); t3.start(); t4.start(); } }