BlockingQueue
- BlockingQueue不接受空元素。实现在试图添加、放置或提供空值时抛出NullPointerException。null用作标记值,表示轮询操作失败。
- 容量有一个最大值限制,如果超过这个容量,则会阻塞,(但不是必须设置最大容量,例如,PriorityBlockQueue 不设置最大值,但受限于数组的最大容量)
- 记住 BlockingQueue 不能插入 null, add, offer, put
- 其中,尽量不要使用 remove 方法,因为要利用循环删除元素,并且还要进行前移元素,因此开销较大。并且 remove 每次只能删除一个元素。
add
如果可以在不超过队列容量的情况下立即插入指定的元素,成功后返回true,如果队列已满则抛出IllegalStateException,则在此队列的末尾插入指定的元素。
public boolean add(E e) {
return super.add(e);
}
Offer
如果队列未满,则插入,如果已满,则返回 false
如果 插入元素 为 null ; 则抛出异常
/**
* Inserts the specified element at the tail of this queue if it is
* possible to do so immediately without exceeding the queue's capacity,
* returning {@code true} upon success and {@code false} if this queue
* is full. This method is generally preferable to method {@link #add},
* which can fail to insert an element only by throwing an exception.
*
* @throws NullPointerException if the specified element is null
*/
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(); // 释放锁
}
}
offer
插入,提供阻塞时长
/**
* Inserts the specified element at the tail of this queue, waiting
* up to the specified wait time for space to become available if
* the queue is full.
*
* @throws InterruptedException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
checkNotNull(e);
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length) {
if (nanos <= 0)
return false;
nanos = notFull.awaitNanos(nanos);
}
enqueue(e);
return true;
} finally {
lock.unlock();
}
}
checkNotNull
查看当前是否为空
private static void checkNotNull(Object v) {
if (v == null)
throw new NullPointerException();
}
enqueue
插入元素,操作实在有锁的前提下
notEmpty.signal(); 还未解决
/**
* Inserts element at current put position, advances, and signals.
* Call only when holding lock.
*/
private void enqueue(E x) {
// assert lock.getHoldCount() == 1;
// assert items[putIndex] == null;
final Object[] items = this.items;
// putIndex 是当前要插入的元素
items[putIndex] = x;
/*
然后把 putIndex ++ ; 因为是循环链表,因此要把 putIndex = 0; 不用担心
putIndex == takeIndex, 因为 在 offer 中已经判断 count == item.size() 是否满了,返 回false
*/
if (++putIndex == items.length)
putIndex = 0;
count++; // 容量自增
notEmpty.signal(); //
}
put
插入
/**
* Inserts the specified element at the tail of this queue, waiting
* for space to become available if the queue is full.
*
* @throws InterruptedException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
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(); // 释放锁
}
}
poll
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 如果 为空,则返回 null
return (count == 0) ? null : dequeue();
} finally {
lock.unlock();
}
}
dequeue
删除元素,并且把当前元素赋值为 null ; 并且 takeIndex ++;
/**
* Extracts element at current take position, advances, and signals.
* Call only when holding lock.
*/
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;
}
remainingCapacity
该方法是为了找出队列剩余容量
返回此队列在理想情况下(在没有内存或资源约束的情况下)可以不阻塞地接受的附加元素的数量;
虽然该方法时线程安全的,但是如果希望通过该方法查询是否有空余元素,然后再插入或者删除,就变成了符合操作,也就是非线程安全的。
// this doc comment is a modified copy of the inherited doc comment,
// without the reference to unlimited queues.
/**
* Returns the number of additional elements that this queue can ideally
* (in the absence of memory or resource constraints) accept without
* blocking. This is always equal to the initial capacity of this queue
* less the current {@code size} of this queue.
*
* <p>Note that you <em>cannot</em> always tell if an attempt to insert
* an element will succeed by inspecting {@code remainingCapacity}
* because it may be the case that another thread is about to
* insert or remove an element.
*/
public int remainingCapacity() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return items.length - count;
} finally {
lock.unlock();
}
}
take
如果,队列为空,则会陷入阻塞
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}
remove
/**
* Removes a single instance of the specified element from this queue,
* if it is present. More formally, removes an element {@code e} such
* that {@code o.equals(e)}, if this queue contains one or more such
* elements.
* Returns {@code true} if this queue contained the specified element
* (or equivalently, if this queue changed as a result of the call).
*
* <p>Removal of interior elements in circular array based queues
* is an intrinsically slow and disruptive operation, so should
* be undertaken only in exceptional circumstances, ideally
* only when the queue is known not to be accessible by other
* threads.
*
* @param o element to be removed from this queue, if present
* @return {@code true} if this queue changed as a result of the call
*/
public boolean remove(Object o) {
if (o == null) return false;
final Object[] items = this.items;
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count > 0) {
final int putIndex = this.putIndex;
int i = takeIndex;
// 通过循环找出 元素,然后确定下标
do {
if (o.equals(items[i])) {
removeAt(i);
return true;
}
if (++i == items.length)
i = 0;
} while (i != putIndex);
}
return false;
} finally {
lock.unlock();
}
}
/**
* Deletes item at array index removeIndex.
* Utility for remove(Object) and iterator.remove.
* Call only when holding lock.
*/
void removeAt(final int removeIndex) {
// assert lock.getHoldCount() == 1;
// assert items[removeIndex] != null;
// assert removeIndex >= 0 && removeIndex < items.length;
final Object[] items = this.items;
if (removeIndex == takeIndex) {
// 如果当前 就是 应该弹出的元素
// removing front item; just advance
items[takeIndex] = null;
if (++takeIndex == items.length)
takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
} else {
// an "interior" remove
// slide over all others up through putIndex.
final int putIndex = this.putIndex;
//
for (int i = removeIndex;;) {
int next = i + 1;
if (next == items.length)
next = 0;
if (next != putIndex) {
// 如果 下一个元素不是最后一个
items[i] = items[next];
i = next;
} else {
items[i] = null;
this.putIndex = i;
break;
}
}
count--;
if (itrs != null)
itrs.removedAt(removeIndex);
}
notFull.signal();
}
toArray(无参)
这个函数比较有意思,是把当前队列中的元素复制到一个新数组中,并不是队列的容量大小,而是队列的真实元素个数
/**
* Returns an array containing all of the elements in this queue, in
* proper sequence.
*
* <p>The returned array will be "safe" in that no references to it are
* maintained by this queue. (In other words, this method must allocate
* a new array). The caller is thus free to modify the returned array.
*
* <p>This method acts as bridge between array-based and collection-based
* APIs.
*
* @return an array containing all of the elements in this queue
*/
public Object[] toArray() {
Object[] a;
final ReentrantLock lock = this.lock;
lock.lock();
try {
final int count = this.count;
a = new Object[count];
// 这里的 n 表示从 takeIndex 到 末尾的元素的个数
int n = items.length - takeIndex;
//
if (count <= n)
// 表示 putIndex 在 takeIndex 之后
System.arraycopy(items, takeIndex, a, 0, count);
else {
// 表示 putIndex 在 takeIndex 之前
System.arraycopy(items, takeIndex, a, 0, n);
System.arraycopy(items, 0, a, n, count - n);
}
} finally {
lock.unlock();
}
return a;
}
toArray
(T[] a)
这个有参数的 可以运行时获取数组类型的对象,而不是返回 Object 类型的数组,因为我们知道将 Object 的数组转化成其他数组 会出现异常
public <T> T[] toArray(T[] a) {}
要记住 返回值的类型必须和参数类型相同,因此,
可以利用 T 类型或者T类型的父类类型数组接受
a = (T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), count);
可以看到 a.getClass().getCOmponentType()
其实是获取元素的真实类型,也就是说有可能是 T 类型的子类,但是又强制类型转换成了 T[];
if (len < count) 如果 参数数组的长度小于 count ;则重新分配
if(len > count) ; 如果 参数数组容量大于 count, 则记得把最后一个元素设置成 null; 用于记录最后一个元素
clear
其实,这个方法就是简单的清空数据元素, 但有一点 takeIndex 和 putIndex 不一定等于 0
for (; k > 0 && lock.hasWaiters(notFull); k--) notFull.signal();
其实,我们知道,其实阻塞队列是基于条件队列实现的, 当 数组存储满的时候, notFull.await
但是,take 数组之后,就需要 notFull.signal()
clear 其实就是一种 出队列,因此,将 notFull 阻塞队列的元素唤醒。
/**
* Atomically removes all of the elements from this queue.
* The queue will be empty after this call returns.
*/
public void clear() {
final Object[] items = this.items;
final ReentrantLock lock = this.lock;
lock.lock();
try {
int k = count;
if (k > 0) {
final int putIndex = this.putIndex;
int i = takeIndex;
do {
items[i] = null;
if (++i == items.length)
i = 0;
} while (i != putIndex);
takeIndex = putIndex;
count = 0;
if (itrs != null)
itrs.queueIsEmpty();
for (; k > 0 && lock.hasWaiters(notFull); k--)
notFull.signal();
}
} finally {
lock.unlock();
}
}