首页 > 代码库 > ArrayBlockingQueue take()和poll()等方法的小区别

ArrayBlockingQueue take()和poll()等方法的小区别

最近工作中看见一个同事的代码是关于ArrayBlockingQueue方面的使用,然后引出take()和poll()的小小的区别,当然他实现方式是没有错.但是由于选择不当有性能的开销,所以我想这里整理一下关于ArrayBlockingQueue 的理解,纯技术交流.有不正确的地方请谅解.下面对源码做一个导读.首先
ArrayBlockingQueue是一个基于数组、先进先出、线程安全的集合类,其特点是实现指定时间的阻塞读写,并且容量有界的。

1) 构造函数        
  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(); 
}

初始化锁和两个锁上的Condition,一个为notEmpty,一个为notFull

2. offer添加
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();---一定要释放 
        } 
    }

这个方法将元素插入数组的末尾,如果数组满,则进入等待,只到以下三种情况发生才继续:
被唤醒、达到指定的时间、当前线程被中断。
该方法首先将等待时间转换成纳秒。然后加锁,如果数组未满,则在末尾插入数据,如果数组已满,则调用notFull.awaitNanos进行等待。如果被唤醒或超时,重新判断是否满。如果线程被interrupt,则直接抛出异常。同时还可以选择put方法,此方法在数组已满的情况下会一直等待,知道数组不为空或线程被interrupt.

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)--本质是使用wait/notify机制就可以避免这些无谓的轮询,节省CPU的消耗 
                    notFull.await(); 
            } catch (InterruptedException ie) { 
                notFull.signal(); // propagate to non-interrupted thread 
                throw ie; 
            } 
            insert(e); 
        } finally { 
            lock.unlock(); 
        } 
    }

接下来介绍重点的一对方法
  public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return (count == 0) ? null : extract();----空直接返回null,那么我们如何判定集合中的数据状况呢?我们就采用重入锁内部通过Sync轮询机制。这样是非常耗费CPU的.
        } finally {
            lock.unlock();
        }
    }
       } 
        } finally { 
            lock.unlock(); 
        } 
    }

  public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await();---为空阻塞.本质是使用wait/notify机制就可以避免这些无谓的轮询,节省CPU的消耗 

            return extract();
        } finally {
            lock.unlock();
        }
    }

ArrayBlockingQueue take()和poll()的一点区别
使用take()函数,如果队列中没有数据,则线程wait释放CPU,而poll()则不会等待,直接返回null;同样,空间耗尽时offer()函数不会等待,直接返回false,而put()则会wait,因此如果你使用while(true)来获得队列元素,千万别用poll(),CPU会100%的.