再提生产者消费者,引入Condition和ReentrantLock

来源:互联网 发布:java接口的意义 编辑:程序博客网 时间:2024/06/07 04:48

之前写过一篇关于生产者消费者模式的文章,最近在复习java线程,又加深了一定程度的理解


这是之前的文章 http://blog.csdn.net/donggua6/article/details/10160695


下面为阻塞队列代码的实现:


class SyncStack {      int index = 0;      WoTou[] arrWT = new WoTou[6];        public synchronized void push(WoTou wt) {  //其实这里不仅仅是为了避免异常的情况,也是为了防止由于操作系统引起的假唤醒(在windows和linux上均存在),//为了避免不是程序主动引发的线程唤醒,说以在wait执行后最好是要检查一下是否符合业务条件        while (index == arrWT.length) {  //while循环避免异常的情况            try {                  this.wait();  //当前线程处于释放当前获得的锁并进入等待状态,直到其他线程调用notify或者是notifAll来唤醒此线程            } catch (InterruptedException e) {                  e.printStackTrace();              }          }          this.notifyAll();  //唤醒正处于wait的线程。(注意,这里无论是notifyAll还是notify都是唤醒等待的线程,也就是说可能唤醒push方法内等待的线程也可能是唤醒pop方法内等待的线程,没有针对性也就是语义不明确,但是在这个阻塞队列的例子中不会出现这种情况)        arrWT[index] = wt;          System.out.println("set: " + arrWT[index]);          index++;      }        public synchronized WoTou pop() {          while (index == 0) {              try {                  this.wait();              } catch (InterruptedException e) {                  e.printStackTrace();              }          }          this.notifyAll();          index--;          System.out.println("get :" + arrWT[index]);          return arrWT[index];      }  }  



为了解决上述notifyAll()这种语法的语义不清楚,下面引用JDK当中的Condition,Condition可以明确的阻塞或者唤醒某个方法的等待或者通知某个方法内阻塞的线程。。下面引用jdk api文档中的例子和解释:
Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。 条件(也称为条件队列 或条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式 释放相关的锁,并挂起当前线程,就像 Object.wait 做的那样。 Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法。 作为一个示例,假定有一个绑定的缓冲区,它支持 put 和 take 方法。如果试图在空的缓冲区上执行 take 操作,则在某一个项变得可用之前,线程将一直阻塞;如果试图在满的缓冲区上执行 put 操作,则在有空间变得可用之前,线程将一直阻塞。我们喜欢在单独的等待 set 中保存 put 线程和 take 线程,这样就可以在缓冲区中的项或空间变得可用时利用最佳规划,一次只通知一个线程。可以使用两个 Condition 实例来做到这一点。  
class BoundedBuffer {   final Lock lock = new ReentrantLock();   final Condition notFull  = lock.newCondition();    final Condition notEmpty = lock.newCondition();    final Object[] items = new Object[100];   int putptr, takeptr, count;   public void put(Object x) throws InterruptedException {     lock.lock();     try {       while (count == items.length)          notFull.await();       items[putptr] = x;        if (++putptr == items.length) putptr = 0;       ++count;       notEmpty.signal();     } finally {       lock.unlock();     }   }   public Object take() throws InterruptedException {     lock.lock();     try {       while (count == 0)          notEmpty.await();       Object x = items[takeptr];        if (++takeptr == items.length) takeptr = 0;       --count;       notFull.signal();       return x;     } finally {       lock.unlock();     }   }  }

 (ArrayBlockingQueue 类提供了这项功能,因此没有理由去实现这个示例类。) Condition 实现可以提供不同于 Object 监视器方法的行为和语义,比如受保证的通知排序,或者在执行通知时不需要保持一个锁。如果某个实现提供了这样特殊的语义,则该实现必须记录这些语义。 注意,Condition 实例只是一些普通的对象,它们自身可以用作 synchronized 语句中的目标,并且可以调用自己的 wait 和 notification 监视器方法。获取 Condition 实例的监视器锁或者使用其监视器方法,与获取和该 Condition 相关的 Lock 或使用其 waiting 和 signalling 方法没有什么特定的关系。为了避免混淆,建议除了在其自身的实现中之外,切勿以这种方式使用 Condition 实例。  

0 0
原创粉丝点击