ArrayBlockingQueue

来源:互联网 发布:itools.cn是什么软件 编辑:程序博客网 时间:2024/05/16 04:59

    在《生产者消费者模型你知道多少》中简单的模拟了一个生产者消费者模型。有些网友对我的实现提出了很多质疑。我在文章的结尾也对抛出了一个问题:在添加的过程中可能出现数据丢失的情况,应该怎么处理?在代码中也充斥了大量的锁,有些锁是不需要的,而且有重复制造轮子的嫌疑。在今天我将引入ArrayBlockingQueue重写这个模型,这在实际开发中可能更有意义,另外对于Java.util.concurrent这个包有一个认识。这个包在并发编程实践里面有非常重要的作用。模型我只会把代码贴出来 ,不再做具体的分析,因为在第一篇中有关于思想性的东西讲的比较多。我会重点讲一下ArrayBlockingQueue,举ArrayBlockingQueue其中的几个方法进行简要的分析。


Queue

        在写代码之前我先给出java.util.concurrent包中所有Queue的UML结构图,有兴趣的同学也可以去看其中的源码,了解其中的一些实现,在实际编程中可以避免重复制造轮子,另一方面也可以学习大牛的设计和代码实现。下面我在写模型中主要会用到其中的ArrayBlockingQueue作为容器(对应的是我上一篇中Container这个类)。这个队列里面就有我今天要写模型里面所有需要的方法。


代码展示

        从代码来看会发现代码量比在第一篇中简少了很多,大量的实现ArrayBlockingQueue已经做掉了,包括判空,线程挂起等操作都封装在ArrayBlockingQueue中。这样具体的应用者的代码会少很多。生产者只需要关心生产,消费者只需要关心消费。而在我的第一篇中具体的生产者还需要去通知消费者,还需要关心整个容器是否满了。从这里可以看出ArrayBlockingQueue是一种比较好的实现方式,高度的内聚,和我的实现有着天壤之别。

Producer.java

[java] view plain copy
  1. /** 
  2.  * 生产者 
  3.  * @author 百恼 2013-11-16下午07:44:36 
  4.  * 
  5.  */  
  6. public class Producer implements Runnable{  
  7.       
  8.     //容器  
  9.     private final ArrayBlockingQueue<Bread> queue;  
  10.       
  11.     public Producer(ArrayBlockingQueue<Bread> queue){  
  12.         this.queue = queue;  
  13.     }  
  14.   
  15.     /* (non-Javadoc) 
  16.      * @see java.lang.Runnable#run() 
  17.      */  
  18.     @Override  
  19.     public void run() {  
  20.         while(true){  
  21.             produce();  
  22.         }  
  23.     }  
  24.       
  25.     public void produce(){  
  26.         /** 
  27.          * put()方法是如果容器满了的话就会把当前线程挂起 
  28.          * offer()方法是容器如果满的话就会返回false,也正是我在前一篇中实现的那种策略。 
  29.          */  
  30.         try {  
  31.             Bread bread = new Bread();  
  32.             queue.put(bread);  
  33.             System.out.println("Producer:"+bread);  
  34.         } catch (InterruptedException e) {  
  35.             e.printStackTrace();  
  36.         }  
  37.     }  
  38. }  

Consumer.java

[java] view plain copy
  1. /** 
  2.  * 消费者 
  3.  * @author 百恼  2013-11-16下午07:42:08 
  4.  * 
  5.  */  
  6. public class Consumer implements Runnable{  
  7.       
  8.     //容器  
  9.     private final ArrayBlockingQueue<Bread> queue;  
  10.       
  11.     public Consumer(ArrayBlockingQueue<Bread> queue){  
  12.         this.queue = queue;  
  13.     }  
  14.   
  15.     /* (non-Javadoc) 
  16.      * @see java.lang.Runnable#run() 
  17.      */  
  18.     @Override  
  19.     public void run() {  
  20.         while(true){  
  21.             consume();  
  22.         }  
  23.     }  
  24.       
  25.     public void consume(){  
  26.         /** 
  27.          * take()方法和put()方法是对应的,从中拿一个数据,如果拿不到线程挂起 
  28.          * poll()方法和offer()方法是对应的,从中拿一个数据,如果没有直接返回null 
  29.          */  
  30.         try {  
  31.             Bread bread = queue.take();  
  32.             System.out.println("consumer:"+bread);  
  33.         } catch (InterruptedException e) {  
  34.             e.printStackTrace();  
  35.         }  
  36.     }  
  37. }  

Client.java

[java] view plain copy
  1. /** 
  2.  * TODO Comment of Client 
  3.  * @author 百恼 2013-11-16下午07:58:38 
  4.  * 
  5.  */  
  6. public class Client {  
  7.   
  8.     /** 
  9.      * @param args 
  10.      */  
  11.     public static void main(String[] args) {  
  12.         int capacity = 10;  
  13.         ArrayBlockingQueue<Bread> queue = new ArrayBlockingQueue<Bread>(capacity);  
  14.   
  15.         new Thread(new Producer(queue)).start();  
  16.         new Thread(new Producer(queue)).start();  
  17.         new Thread(new Consumer(queue)).start();  
  18.         new Thread(new Consumer(queue)).start();  
  19.         new Thread(new Consumer(queue)).start();  
  20.     }  
  21.   
  22. }  

ArrayBlockingQueue简要分析


ConditionObject

        如果看了源码的可以看出ArrayBlockingQueue并没有用List作为容器,而是用了数组。我在第一篇中的实现就直接用了List去实现。这样肯定是为了性能去考虑。另外一个就是线程监听器的实现用的Condition,具体的是ConditionObject这个类。核心是LockSupport.park()LockSupport.unpark()(参考LockSupport源码分析)。


remoteAt方法

         
         在ArrayBlockingQueue里面用了一个for循环然后删除节点为i的数据,如果还记得ArrayList的remove会发现,它里面直接用了System.arraycopy()方法进行删除其中的一条数据。谁的性能好一些,也没有实测过,在这里就不妄下结论。

poll方法

        外在ArrayBlockingQueue中有这个poll(long timeout, TimeUnit unit)这个方法,这个方法是传入一个参数就是等待timeout ms后返回。实际应用中会发现这个等待时间是不准确的。我在一次对接口测试的过程中发现了这样一个问题。


总结

       这一篇中主要讲了用ArrayBlockingQueue实现生产者消费者模型,这个实现的本身意义不大,更大的意义是带入到java.util.concurrent包中去。可能对于一些人来说这个包比较陌生,即使用过这个包去编程的人也可能只限于AtomicInteger,ArrayBlockingQueue,CountDownLatch这些。就好像容器常用的也就ArrayList,HashSet,HashMap这些一样。对于TreeMap,ConcurrentHashMap这些容器可能知道的就是太多了。我在这里也提供一个入口。至于这个模型我觉得更需要关心的是其中的思想,而不是实现本身。


附上源码:

 

public class ArrayBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable {      /** 队列元素 数组 */      private final E[] items;      /** 获取、删除元素时的索引(take, poll 或 remove操作) */      private int takeIndex;      /** 添加元素时的索引(put, offer或 add操作) */      private int putIndex;      /** 队列元素的数目*/      private int count;        /** 锁 */      private final ReentrantLock lock;      /** 获取操作时的条件 */      private final Condition notEmpty;      /** 插入操作时的条件 */      private final Condition notFull;      //超出数组长度时,重设为0      final int inc(int i) {          return (++i == items.length)? 0 : i;      }      /**      * 插入元素(在获得锁的情况下才调用)      */      private void insert(E x) {          items[putIndex] = x;          putIndex = inc(putIndex);          ++count;          notEmpty.signal();      }      /**      * 获取并移除元素(在获得锁的情况下才调用)      */      private E extract() {          final E[] items = this.items;          E x = items[takeIndex];          items[takeIndex] = null;          takeIndex = inc(takeIndex);//移到下一个位置          --count;          notFull.signal();          return x;      }      /**      * 删除i位置的元素      */      void removeAt(int i) {          final E[] items = this.items;          // if removing front item, just advance          if (i == takeIndex) {              items[takeIndex] = null;              takeIndex = inc(takeIndex);          } else {              // 把i后面的直到putIndex的元素都向前移动一个位置              for (;;) {                  int nexti = inc(i);                  if (nexti != putIndex) {                      items[i] = items[nexti];                      i = nexti;                  } else {                      items[i] = null;                      putIndex = i;                      break;                  }              }          }          --count;          notFull.signal();      }      /**      *构造方法,指定容量,默认策略(不是按照FIFO的顺序访问)      */      public ArrayBlockingQueue(int capacity) {          this(capacity, false);      }      /**      *构造方法,指定容量及策略      */      public ArrayBlockingQueue(int capacity, boolean fair) {          if (capacity <= 0)              throw new IllegalArgumentException();          this.items = (E[]) new Object[capacity];          lock = new ReentrantLock(fair);          notEmpty = lock.newCondition();          notFull =  lock.newCondition();      }      /**      * 通过集合构造      */      public ArrayBlockingQueue(int capacity, boolean fair,                                Collection<? extends E> c) {          this(capacity, fair);          if (capacity < c.size())              throw new IllegalArgumentException();          for (Iterator<? extends E> it = c.iterator(); it.hasNext();)              add(it.next());      }      /**      * 插入元素到队尾(super调用offer方法)      *  public boolean add(E e) {      *    if (offer(e))      *        return true;      *    else      *       throw new IllegalStateException("Queue full");      *  }      * 将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量),      * 在成功时返回 true,如果此队列已满,则抛出 IllegalStateException。      */      public boolean add(E e) {   return super.add(e);      }      /**      * 将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量),      * 在成功时返回 true,如果此队列已满,则返回 false。      */      public boolean offer(E e) {          if (e == null) throw new NullPointerException();          final ReentrantLock lock = this.lock;          lock.lock();          try {              if (count == items.length)                  return false;              else {                  insert(e);                  return true;              }          } finally {              lock.unlock();          }      }      /**      * 将指定的元素插入此队列的尾部,如果该队列已满,则等待可用的空间。      */      public void put(E e) throws InterruptedException {          if (e == null) throw new NullPointerException();          final E[] items = this.items;          final ReentrantLock lock = this.lock;          lock.lockInterruptibly();          try {              try {                  while (count == items.length)                      notFull.await();              } catch (InterruptedException ie) {                  notFull.signal(); // propagate to non-interrupted thread                  throw ie;              }              insert(e);          } finally {              lock.unlock();          }      }      /**      * 将指定的元素插入此队列的尾部,如果该队列已满,则在到达指定的等待时间之前等待可用的空间。      */      public boolean offer(E e, long timeout, TimeUnit unit)          throws InterruptedException {          if (e == null) throw new NullPointerException();   long nanos = unit.toNanos(timeout);          final ReentrantLock lock = this.lock;          lock.lockInterruptibly();          try {              for (;;) {                  if (count != items.length) {                      insert(e);                      return true;                  }                  if (nanos <= 0)//如果时间到了就返回                      return false;                  try {                      nanos = notFull.awaitNanos(nanos);                  } catch (InterruptedException ie) {                      notFull.signal(); // propagate to non-interrupted thread                      throw ie;                  }              }          } finally {              lock.unlock();          }      }      //获取并移除此队列的头,如果此队列为空,则返回 null。      public E poll() {          final ReentrantLock lock = this.lock;          lock.lock();          try {              if (count == 0)                  return null;              E x = extract();              return x;          } finally {              lock.unlock();          }      }      //获取并移除此队列的头部,在元素变得可用之前一直等待(如果有必要)。      public E take() throws InterruptedException {          final ReentrantLock lock = this.lock;          lock.lockInterruptibly();          try {              try {                  while (count == 0)                      notEmpty.await();              } catch (InterruptedException ie) {                  notEmpty.signal(); // propagate to non-interrupted thread                  throw ie;              }              E x = extract();              return x;          } finally {              lock.unlock();          }      }      //获取并移除此队列的头部,在指定的等待时间前等待可用的元素(如果有必要)。      public E poll(long timeout, TimeUnit unit) throws InterruptedException {   long nanos = unit.toNanos(timeout);          final ReentrantLock lock = this.lock;          lock.lockInterruptibly();          try {              for (;;) {                  if (count != 0) {                      E x = extract();                      return x;                  }                  if (nanos <= 0)                      return null;                  try {                      nanos = notEmpty.awaitNanos(nanos);                  } catch (InterruptedException ie) {                      notEmpty.signal(); // propagate to non-interrupted thread                      throw ie;                  }              }          } finally {              lock.unlock();          }      }      //获取但不移除此队列的头;如果此队列为空,则返回 null。      public E peek() {          final ReentrantLock lock = this.lock;          lock.lock();          try {              return (count == 0) ? null : items[takeIndex];          } finally {              lock.unlock();          }      }      /**      * 返回此队列中元素的数量。      */      public int size() {          final ReentrantLock lock = this.lock;          lock.lock();          try {              return count;          } finally {              lock.unlock();          }      }      /**      *返回在无阻塞的理想情况下(不存在内存或资源约束)此队列能接受的其他元素数量。      */      public int remainingCapacity() {          final ReentrantLock lock = this.lock;          lock.lock();          try {              return items.length - count;          } finally {              lock.unlock();          }      }      /**      * 从此队列中移除指定元素的单个实例(如果存在)。      */      public boolean remove(Object o) {          if (o == null) return false;          final E[] items = this.items;          final ReentrantLock lock = this.lock;          lock.lock();          try {              int i = takeIndex;              int k = 0;              for (;;) {                  if (k++ >= count)                      return false;                  if (o.equals(items[i])) {                      removeAt(i);                      return true;                  }                  i = inc(i);              }          } finally {              lock.unlock();          }      }      /**      * 如果此队列包含指定的元素,则返回 true。      */      public boolean contains(Object o) {          if (o == null) return false;          final E[] items = this.items;          final ReentrantLock lock = this.lock;          lock.lock();          try {              int i = takeIndex;              int k = 0;              while (k++ < count) {                  if (o.equals(items[i]))                      return true;                  i = inc(i);              }              return false;          } finally {              lock.unlock();          }      }    }