ArrayBlockingQueue实现

   ArrayBlockingQueue是基于数组实现的有序队列。

   ArrayBlockingQueue实现元素添加有下面三个方法:

                                   public boolean add(E e)

                                   public boolean offer(E e)

                                    public void put(E e) throws InterruptedException

   获取元素也有下面思个方法:

                                  public E remove()

                                  public E poll()

                                  public E take() throws InterruptedException

                                  public E peek() 

   那么这些方法有什么区别呢?

   

public class ArrayBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {

    /**
     * Serialization ID. This class relies on default serialization
     * even for the items array, which is default-serialized, even if
     * it is empty. Otherwise it could not be declared final, which is
     * necessary here.
     */
    private static final long serialVersionUID = -817911632652898426L;

    /** The queued items */
    final Object[] items;

    /** items index for next take, poll, peek or remove */
    int takeIndex;

    /** items index for next put, offer, or add */
    int putIndex;
     上面的代码中我们可以看到Object[] 用来存放元素,是基于数组结构。

     takeIndex 用来存放下一个可以获取的元素的下标。

     putIndex 用来存放下一个可以添加的元素的下标。


    一:offer方法:

   

 public boolean offer(E e) {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            if (count == items.length)
                return false;
            else {
                enqueue(e);
                return true;
            }
        } finally {
            lock.unlock();
        }
    }

 private static void checkNotNull(Object v) {
        if (v == null)
            throw new NullPointerException();
    }
   offer方法很简单

  1:首先检查元素是否非空,如果添加的元素为null,则抛出NullPointerException

  2:判断元素队列中的个数是否等于数组长度,如果是 ,则返回false。

  3:前面两步不满足,则进行入队操作。

  

 private void enqueue(E x) {
        // assert lock.getHoldCount() == 1;
        // assert items[putIndex] == null;
        final Object[] items = this.items;
        items[putIndex] = x;
        if (++putIndex == items.length)
            putIndex = 0;
        count++;
        notEmpty.signal();
    }
    入队方法也很简单,直接将元素置入数组相应位置。然后对putIndex进行++

    如果putIndex的值等于数组长度则置0,因为putIndex和takeIndex是循环利用的。

    最后一行通知Condition  notEmpty信号量。


   从上面我们可以看到offer和普通的队列入队操作,并无其他不同,看不出Blocking的作用。

  

  二:add方法:

     

public boolean add(E e) {
        if (offer(e))
            return true;
        else
            throw new IllegalStateException("Queue full");
    }

   add方法调用offer方法,如果调用成功,则返回true,否则抛出IllegalStateException("Queue full")异常。


   三:put方法:

    

public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length)
                notFull.await();
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }

     put添加元素时,如果队列已满,此时会调用notFull.await,阻塞线程。直到有元素出队列,dequeue()方法执行,调用notFull.signal();

    

 private E dequeue() {
        // assert lock.getHoldCount() == 1;
        // assert items[takeIndex] != null;
        final Object[] items = this.items;
        @SuppressWarnings("unchecked")
        E x = (E) items[takeIndex];
        items[takeIndex] = null;
        if (++takeIndex == items.length)
            takeIndex = 0;
        count--;
        if (itrs != null)
            itrs.elementDequeued();
        notFull.signal();
        return x;
    }


    从上面的源码我们可以看到三个添加方法的区别:

    offer  添加元素入队列,如果队列满,则返回false

    add   添加元素入队列,如果队列满,则抛出异常

    put   添加元素入队列,如果队列满,则阻塞当前线程,直到队列有其他元素出队列,执行入队。


    接下去我们看出队方法:

    一:poll方法

    

public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return (count == 0) ? null : dequeue();
        } finally {
            lock.unlock();
        }
    }
   

   poll方法和offer方法有点类型,如果队列为空,则返回null,否则执行元素出队操作。

  

  二:remove方法

       

public E remove() {
        E x = poll();
        if (x != null)
            return x;
        else
            throw new NoSuchElementException();
    }
    remove调用poll执行元素出队,如果队列为空,则抛出NoSuchElementException异常。


  三:peek方法

  

public E peek() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return itemAt(takeIndex); // null when queue is empty
        } finally {
            lock.unlock();
        }
    }
  

   peek方法并没有执行元素出队方法,只是返回当前takeindex元素的值。


  四:take方法

       

public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await();
            return dequeue();
        } finally {
            lock.unlock();
        }
    }

   当队列个数为0时,take方法会阻塞。直到enqueue方法执行入队操作,调用 notEmpty.signal();


   上面我们可以看到四个取元素的区别

   poll  队列为空,则返回false 否则执行出队操作

   remove  队列为空,则抛出异常 否则执行出队操作

   peek   只是返回队列头元素,并不会执行出队操作

   take  队列为空,则线程阻塞,直到有元素入队,执行出队操作。

猜你喜欢

转载自blog.csdn.net/shizhan1881/article/details/69855682