最近工作中看见一个同事的代码是关于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%的.
ArrayBlockingQueue take()和poll()等方法的小区别