一、概述
LinkedBlockingQueue 是一个基于单链表的无界阻塞队列,它跟 ArrayBlockingQueue 一样都是通过使用 ReentrantLock 来保证线程安全的。
LinkedBlockingQueue 不允许null值,也不强制我们指定队列得初始容量,默认的容量为 Integer.MAX_VALUE。
二、属性
//链表节点的结构,从这个类我们可以看出,它采用的是单链表结构
static class Node<E> {
E item;
Node<E> next;
Node(E x) { item = x; }
}
//队列的最大长度
private final int capacity;
//队列中元素的个数
private final AtomicInteger count = new AtomicInteger();
//指向链表的头节点
transient Node<E> head;
//指向链表的尾节点
private transient Node<E> last;
//出队锁
private final ReentrantLock takeLock = new ReentrantLock();
private final Condition notEmpty = takeLock.newCondition();
//入队锁
private final ReentrantLock putLock = new ReentrantLock();
private final Condition notFull = putLock.newCondition();
三、方法
1、基本方法
下面的代码是入队和出队操作的实现,我们可以看到,LinkedBlockingQueue 通过一个队头指针和一个队尾指针来保证入队和出队的复杂度都为O(1)。
//入队
private void enqueue(Node<E> node) {
last = last.next = node;
}
//出队
private E dequeue() {
Node<E> h = head;
Node<E> first = h.next;
h.next = h; // help GC
head = first;
E x = first.item;
first.item = null;
return x;
}
跟 ArrayBlockingQueue 一样,LinkedBlockingQueue 也提供了删除指定节点的操作来满足删除指定对象的需求,源码如下:
//删除节点p
void unlink(Node<E> p, Node<E> trail) {
p.item = null;
trail.next = p.next;
if (last == p)
last = trail;
if (count.getAndDecrement() == capacity)
notFull.signal();
}
2、入队操作
LinkedBlockingQueue 提供了多种入队操作的实现来满足不同情况下的需求,入队操作有如下几种:
public boolean add(E e);
public void put(E e) throws InterruptedException;
public boolean offer(E e);
public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException;
下面是几种入队操作的具体实现,从源码中我们可以发现,这几种入队操作都直接或间接地调用了enqueue方法,所以只要搞清楚了enqueue方法,就已经没有什么难点了。这几种入队操作的区别如下:
- add(E e):入队成功返回true,入队失败抛出 IllegalStateException 异常。
- put(E e):如果队列满了,会一直等待,直到成功入队。
- offer(E e):入队成功返回true,入队失败返回false。
- offer(E e, long timeout, TimeUnit unit):在指定的时间内一直尝试入队,入队成功返回true,超时返回false。
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
//如果队列满了,则进行等待并释放putLock锁
while (count.get() == capacity) {
notFull.await();
}
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
}
public boolean offer(E e) {
if (e == null) throw new NullPointerException();
final AtomicInteger count = this.count;
//判断队列是否满了
if (count.get() == capacity)
return false;
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
if (count.get() < capacity) {
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
}
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
return c >= 0;
}
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
if (e == null) throw new NullPointerException();
long nanos = unit.toNanos(timeout);
int c = -1;
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
while (count.get() == capacity) {
if (nanos <= 0)
return false;
nanos = notFull.awaitNanos(nanos);
}
enqueue(new Node<E>(e));
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
return true;
}
3、出队操作
同时,LinkedBlockingQueue 也提供了多种出队操作的实现来满足不同情况下的需求,如下:
public E poll();
public E poll(long timeout, TimeUnit unit) throws InterruptedException;
public E take() throws InterruptedException;
下面是几种出队操作的具体实现,与入队操作类似,这几种出队操作都直接或间接地调用了dequeue方法,所以如果搞清楚了前面的dequeue方法,就已经没有什么难点了。这几种出队操作的区别如下:
- take():如果队列为空,就进行等待,直到出队成功,并返回队头元素。
- poll():如果队列不为空,则出队并返回队头元素,否则返回null。
- poll(long timeout, TimeUnit unit):在指定的时间内一直尝试出队,出队成功返回队头元素,否则返回null。
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
//如果队尾为空,则进行等待,直到被非空信号唤醒
while (count.get() == 0) {
notEmpty.await();
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
public E poll() {
final AtomicInteger count = this.count;
if (count.get() == 0)
return null;
E x = null;
int c = -1;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
if (count.get() > 0) {
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
}
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
E x = null;
int c = -1;
long nanos = unit.toNanos(timeout);
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {
if (nanos <= 0)
return null;
nanos = notEmpty.awaitNanos(nanos);
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
4、其他方法
//只获取队头元素,不出队,如果队列为空,则返回null
public E peek() {
if (count.get() == 0)
return null;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
Node<E> first = head.next;
if (first == null)
return null;
else
return first.item;
} finally {
takeLock.unlock();
}
}
//从队列中删除指定的对象
//删除操作最坏情况下会遍历整个链表,所以复杂度为O(n)
public boolean remove(Object o) {
if (o == null) return false;
fullyLock();
try {
for (Node<E> trail = head, p = trail.next;
p != null;
trail = p, p = p.next) {
if (o.equals(p.item)) {
unlink(p, trail);
return true;
}
}
return false;
} finally {
fullyUnlock();
}
}
//判断队列中是否存在该元素
//该操作最坏情况下会遍历整个链表,所以复杂度为O(n)
public boolean contains(Object o) {
if (o == null) return false;
fullyLock();
try {
for (Node<E> p = head.next; p != null; p = p.next)
if (o.equals(p.item))
return true;
return false;
} finally {
fullyUnlock();
}
}
//获取队列中元素的个数
public int size() {
return count.get();
}
//获取队列中剩余的容量
public int remainingCapacity() {
return capacity - count.get();
}
//清空队列,复杂度为O(n)
public void clear() {
fullyLock();
try {
for (Node<E> p, h = head; (p = h.next) != null; h = p) {
h.next = h;
p.item = null;
}
head = last;
// assert head.item == null && head.next == null;
if (count.getAndSet(0) == capacity)
notFull.signal();
} finally {
fullyUnlock();
}
}