阻塞队列
BlockingQueue接口下的各个实现类及部分源码介绍,BlockingQueue 对插入操作、移除操作、获取元素操作提供了四种不同的方法用于不同的场景中使用:
- 抛出异常,如果队列为空或队列已满直接抛异常
- 返回特殊值(null 或 true/false)
- 阻塞等待此操作,直到这个操作成功
- 阻塞等待此操作,直到成功或者超时指定时间。
操作 | 抛异常 | 返回空 | 阻塞等待 | 超时 |
插入 | add(e) | offer(e) | put(e) | offer(e, time, unit) |
移除 | remove() | poll() | take() | poll(time, unit) |
不移除返回头 | element() | peek() | – | – |
BlockingQueue,我们的关注点应该在 put(e) 和 take() 这两个方法,因为这两个方法是带阻塞的
ArrayBlockingQueue
ArrayBlockingQueue 是 BlockingQueue 接口的有界队列实现类,底层采用数组来实现。
put
blockingQueue.put(ball);
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();
}
}
private void enqueue(E x) {
//底层使用数组实现的,数组下标赋值
final Object[] items = this.items;
items[putIndex] = x;
if (++putIndex == items.length)
putIndex = 0;
count++;
//唤醒非空等待,就是唤醒取值等待
notEmpty.signal();
}
blockingQueue.take()
public E take() throws InterruptedException {
//获取锁
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
//非空阻塞
while (count == 0)
notEmpty.await();
//取队列数据
return dequeue();
} finally {
lock.unlock();
}
}
private E dequeue() {
//取数组下标数据 老下标 置空
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();
//唤醒put中的阻塞线程
notFull.signal();
return x;
}
源码很简单吧,多余的不说了
LinkedBlockingQueue
不同于ArrayBlockingQueue,这里用到了两个锁,两个 Condition
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
//用于唤醒读线程取值
int c = -1;
Node<E> node = new Node<E>(e);
//获取put锁
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
//上锁
putLock.lockInterruptibly();
try {
//队列是否满了 满了阻塞notFull
while (count.get() == capacity) {
notFull.await();
}
//加入队列
enqueue(node);
c = count.getAndIncrement();
//队列没有满 唤醒 另一个put线程
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
//如果 c == 0,那么代表队列在这个元素入队前是空的
if (c == 0)
//那么所有的读线程都在等待 notEmpty 这个条件,等待唤醒,这里做一次唤醒操作
signalNotEmpty();
}
enqueue
private void enqueue(Node<E> node) {
last = last.next = node;
}
private void signalNotEmpty() {
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
notEmpty.signal();
} finally {
takeLock.unlock();
}
}
take
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
//take锁
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
//如果队列等于0 阻塞住
while (count.get() == 0) {
notEmpty.await();
}
//取队列数据
x = dequeue();
//取前队列中的长度
c = count.getAndDecrement();
if (c > 1)
//如果队列中有数据唤醒其余线程取数据
notEmpty.signal();
} finally {
takeLock.unlock();
}
// 如果 c == capacity,那么说明在这个 take 方法发生的时候,队列是满的
// 既然出队了一个,那么意味着队列不满了,唤醒写线程去写
if (c == capacity)
signalNotFull();
return x;
}
DelayQueue
由优先级堆支持的、基于时间的调度队列
PriorityBlockingQueue
由优先级堆支持的无界优先级队列
ConcurrentLinkedQueue
底层利用cas + 循环,cas失败进行加一次循环,无界队列。
最终将数据插入到队尾
取值同样也是cas操作,这里就不细说了,大年三十,准备睡觉吃饭吧
public boolean offer(E e) {
checkNotNull(e);
final Node<E> newNode = new Node<E>(e);
for (Node<E> t = tail, p = t;;) {
Node<E> q = p.next;
if (q == null) {
if (p.casNext(null, newNode)) {
if (p != t) // hop two nodes at a time
casTail(t, newNode); // Failure is OK.
return true;
}
}
else if (p == q)
p = (t != (t = tail)) ? t : head;
else
p = (p != t && t != (t = tail)) ? t : q;
}
}