Java同步技术(七)

版权声明

作者联系并注明出处http://blog.csdn.net/Iangao/archive/2008/11/08/3254001.aspx。

三、经典同步问题的Java实现

3.1 有限缓冲区(生产者-消费者)问题

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);}

/*** 取出一个对象,如果没有填充信号,则等待timeout时间* @param timeout 等待填充信号的时间* @return 返回的对象,Null表示缓充区中没有对象*/private T take(long timeout) throws InterruptedException{ T object; // 获取”填充信号”, 失败则返回null if(!full.p(timeout)) return null; // 缓冲区操作(同步) synchronized(buffer){ // buffer临界区:取出对象 object=buffer[here]; buffer[here++]=null; if(here==buffer.length) here=0; } // 发送一个”空位信号” empty.v(); return object;}/*** 取出一个对象,死等填充信号* @return 取出的对象*/public T take() throws InterruptedException{ return deliver(0);}

3.1.2 测试有限缓冲区应用

为了演示有限缓冲区的应用,首先我们创建一个生产者消费者类对其进行测试,类名为ProducerCustomerTest.其中定义两个方法。一个用于不断生产产品并把产品存入缓冲区等待消费的生产者方法,另一个是用于不断从缓冲区中提取产品并对其进行消费的消费者方法。这两个方法将分别在两上线程上运行。然后我们再定义一个方便测试的消费主体Produce产品类。各循环10次。代码清单如下:

/*** 有限缓冲区(生产者-消费者)问题示例* @author iangao*/public class ProducerCusomerTest extends ThreadsTest{ /** * 定义长度为2的”有限缓冲区”,用于用于生产者与消费者之间传递产品。 */ private BlockingQueue<Produce> buffer=new BlockingQueue<Produce>(2); /** * 线程1: 消费者(Consomer)方法, 每3.5秒消费一个产品 */ public void runInThread1() throws InterruptedException{ for(int i=0; i<4; i++){ // 获取消费品(如果没有消费品,就等待有消费品信号时再取) output(“[C]: 获取消费品…”); Produce item=buffer.take(); // 消费一个产品(周期3.5秒) output(“[C]: 消费产品:”+item.getId()+””); sleep(3500); } } /** * 线程2:生产者(Producer)方法,每1秒生产一个 */ public void runInThread2() throws InterruptedException{ // 生产前准备(2秒) sleep(2000); for(int i=0; i<4; i++){ // 生产一个产品(周期1秒) sleep(1000); Produce item=new Produce(); output(“[P]: 生产出”+item.getId()+”, 存入缓冲区…”); // 存入产品,以供消费(如果缓冲区已满就等待有空位信号时再存) buffer.put(item); output(“[P]: “+item.getId()+”已存入!”); } } public static void main(String[] args){ new ProducerCusomerTest().execute(2); // 测试,启动2个线程 }}

/*** 产品类,消费主体*/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

夺冠那一刻,豪情万丈!登顶那一瞬,万众瞩目!那一刻的嫣然一笑,

Java同步技术(七)

相关文章:

你感兴趣的文章:

标签云: