Queue常用类解析之PriorityQueue
Queue常用类解析之ConcurrentLinkedQueue
Queue常用类解析之BlockingQueue(一):PriorityBlockingQueue、DelayQueue和DelayedWorkQueue
Queue常用类解析之BlockingQueue(二):ArrayBlockingQueue
接着上文对BlockingQueue的介绍继续向下
六、LinkedBlockingQueue
LinkedBlockingQueue是链表格式的阻塞队列,记录链表的head和last,head的next指向头结点,last指向尾节点,分别用于出队和入队。
LinkedBlockingQueue有容量限制,因此元素入队和出队都有可能阻塞线程。容量默认为Integer.MAX_VALUE。
LinkedBlockingQueue内部含有两个lock锁,分别用于元素入队和出队,因此元素入队和出队是可以并发的。
1. 属性
/** The capacity bound, or Integer.MAX_VALUE if none */
private final int capacity;
/** Current number of elements */
private final AtomicInteger count = new AtomicInteger();
/**
* Head of linked list.
* Invariant: head.item == null
*/
transient Node<E> head;
/**
* Tail of linked list.
* Invariant: last.next == null
*/
private transient Node<E> last;
/** Lock held by take, poll, etc */
private final ReentrantLock takeLock = new ReentrantLock();
/** Wait queue for waiting takes */
private final Condition notEmpty = takeLock.newCondition();
/** Lock held by put, offer, etc */
private final ReentrantLock putLock = new ReentrantLock();
/** Wait queue for waiting puts */
private final Condition notFull = putLock.newCondition();
属性都比较常规,没有什么特别的。
2. LinkedBlockingQueue#offer(Object, long, TimeUnit)
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;
}
整体流程就是:判空,加putLock锁,判断容量:容量已满的情况下阻塞,入队,更新元素数量,检查并发送notFull信号,释放putLock锁,检查并发送notEmpty信号(需要takeLock锁)。
不明白这儿有必要发送notFull信号,不应该是增加元素发送notEmpty信号,移除元素发送notFull信号吗?
3. LinkedBlockingQueue#enqueue(Node)
private void enqueue(Node<E> node) {
// assert putLock.isHeldByCurrentThread();
// assert last.next == null;
last = last.next = node;
}
具体的入队操作。
节点加载链表尾部,并将新加的节点作为last。
4. LinkedBlockingQueue#poll(long, TimeUnit)
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;
}
整体流程就是:加takeLock锁,判断容量:容量为0的情况下阻塞,出队,更新元素数量,检查并发送notEmpty信号,释放putLock锁,检查并发送notFull信号(需要putLock锁)。
5. LinkedBlockingQueue#dequeue()
private E dequeue() {
// assert takeLock.isHeldByCurrentThread();
// assert head.item == null;
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;
}
head节点从链表中移除,head.next节点作为head节点。
6. LinkedBlockingQueue#remove(Object)
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();
}
}
remove方法也没有什么特别的,只是其有可能同时影响头节点和尾节点,因此同时需要putLock锁和takeLock锁。