本文版权归作者所有,如有转载请与作者联系并注明出处http://blog.csdn.net/Iangao/archive/2008/11/08/3254001.aspx。
三、经典同步问题的Java实现
3.1.1 定义有限缓冲区
生产者-消费者问题描述的是生产者和消费者两个角色之间的交互问题。因为当生产者生产出产品后并不一定会马上会有消费者去消费它,那么就需要一个缓冲区去暂时保存这个产品,这样生产者就可以继续生产了,然而缓冲区一般是有限的,当缓冲区满了的时候,生产者就无法再次向缓冲区中放入生产出的产品了,这时生产者就不得不停下来等待下一次消费到来,等待消费结束空出新的缓冲区后再继续生产。上述情况一般是由于生产快,消费慢造成的。而当生产慢,消费快时,消费者在消费掉所有的缓冲区产品后,就不得不等待生产者现次生产出新的产品后再进行消费了。综上所述由于生产者和消费者两者(两线程)之间存在工作速度上的差异,而通过有限缓冲区却可以很好的协调两者之间同步的工作,因此对缓冲区存取访问的控制也就成为了解决问题的关键。所以我们又把它称为有限缓冲区问题。
下面,我们首先实现一个可以重复利用的有限缓冲区类BlockingQueue,并为其创建两个方法: 一个方法名为put(),用于向缓冲区中存入一个对象,而当缓冲区已满时线程则会处于等待状态直到缓冲区释放出空间.而另一个方法名为consome(),用于从缓冲区中取出一个对象,并释放一个缓冲区空间.而当缓冲区中没有数据时,线程会处于等待状态中直到缓冲区中存入了数据.其实现如下:
/** * 有随缓冲区 * @author iangao */ public class BlockingQueue<T> { private T[] buffer; //缓冲区(循环使用的FIFO栈) private Semaphore full; //满位信号量(计数信号量) private Semaphore empty; //空位信号量(计数信号量) private int here=0; //当前缓冲区指:用于consome一个对象 private int next=0; //下一个缓冲区指针:用于put一个对象 /** * 初始化缓冲区的大小 * @param maxSize 缓冲区大小 */ public BlockingQueue(int maxSize){ buffer=(T[])new Object[maxSize]; full=new Semaphore(0); // 初始满位信号量为0(没有产品) empty=new Semaphore(maxSize); // 初始空位信号量为maxSize(全是空位) } } | |
/** * 存入一个对象,如果没有空位,那么等timeout时间 * @param object 存入的对象 * @param timeout 等待空位信号的时间 * @return true: 存入成功 false:存入失败 */ public boolean put(T object,long timeout) throws InterruptedException{ // 获取"空位信号", 失败则返回false if(!empty.p(timeout)) return false; // 缓冲区操作(同步) synchronized(buffer){ // buffer临界区:加入对象 buffer[next++]=object; if(next==buffer.length) next=0; } // 发出一个"填充信号" full.v(); return true; } /** * 默认操作, 存入一个对象,死等空位信号 * @param object 被存入的对象 */ public boolean put(T object) throws InterruptedException{ return store(object,0); } | /** |
3.1.2 测试有限缓冲区应用
为了演示有限缓冲区的应用,首先我们创建一个生产者消费者类对其进行测试,类名为ProducerCustomerTest.其中定义两个方法。一个用于不断生产产品并把产品存入缓冲区等待消费的生产者方法,另一个是用于不断从缓冲区中提取产品并对其进行消费的消费者方法。这两个方法将分别在两上线程上运行。然后我们再定义一个方便测试的消费主体Produce产品类。各循环10次。代码清单如下:
/** | /** * 产品类,消费主体 */ public class Produce{ private static int seq=0; private int id=0; public Produce(){ // 自增id号,用于测试 this.id=seq++; } public String getName() { return "产品"+id; } } |
测试结果: [C]: 获取消费品... // 缓冲区空,等待 [P]: 生产出产品0, 存入缓冲区... [P]: 产品0已存入! // 有产品了,消费 [C]: 消费产品:产品0 [P]: 生产出产品1, 存入缓冲区... [P]: 产品1已存入! [P]: 生产出产品2, 存入缓冲区... [P]: 产品2已存入! [P]: 生产出产品3, 存入缓冲区... // 缓冲区满,等待 [C]: 获取消费品... [P]: 产品3已存入! // 有空位了,存入 [C]: 消费产品:产品1 [C]: 获取消费品... [C]: 消费产品:产品2 [C]: 获取消费品... [C]: 消费产品:产品3 |
3.1.3 J2SE 1.5中有限缓冲区的实现
在1.5版中,Java语言提供了一个名为java.util.concurrent.BlockingQueue的接口,其中包含有put()和consume()方法,我们可以通过实现这个接口来实现对有限缓冲区的定义。另外库中还为我们提供了一个现成的实现java.util.concurrent.ArrayBlockingQueue