这篇文章主要讲LinkedBlockingQueue。阻塞队列使用链表来完成存储,之前分析的ArrayBlockingQueue是用数组。现在看下具体实现会有什么特点,还是从构造方法出发。
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
//初始化了一个Node。
last = head = new Node<E>(null);
}
跟之前一样,来分析下offer吧。
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();
//再加1,还小于容量,可以释放锁。
if (c + 1 < capacity)
notFull.signal();
}
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
return c >= 0;
}
//将node 放在链尾的末端。
private void enqueue(Node<E> node) {
// assert putLock.isHeldByCurrentThread();
// assert last.next == null;
last = last.next = node;
}
//获取head的内容。
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();
//释放notEmpty锁。
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
来分析下 dequeue()。链表操作。返回了head结点并且head置为空,释放掉。注意写法很考究。
private E dequeue() {
// assert takeLock.isHeldByCurrentThread();
// assert head.item == null;
Node<E> h = head;
Node<E> first = h.next;
//这样原来的头部结点的next也指向自身了。原来next持有别的对象,这样容易gc释放。
h.next = h; // help GC
//head变为了原来的next。
head = first;
E x = first.item;
first.item = null;
return x;
}
其实跟之前分析的ArrayBlockingQueue差别不大。不过是读写分离,利用链表实现。