- 一个常用的并发队列,链表实现
有效构造函数
- 构造函数1
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}
设置了queue的容量,队首=队尾=首节点( new Node<E>(null) )
看一下这个node的结构,一个很简单的链表节点:
static class Node<E> {
E item;
Node<E> next;
Node(E x) { item = x; }
}
- 构造函数2
public LinkedBlockingQueue(Collection<? extends E> c) {
this(Integer.MAX_VALUE);
final ReentrantLock putLock = this.putLock;
putLock.lock(); // Never contended, but necessary for visibility
try {
int n = 0;
for (E e : c) {
if (e == null)
throw new NullPointerException();
if (n == capacity)
throw new IllegalStateException("Queue full");
enqueue(new Node<E>(e));
++n;
}
count.set(n);
} finally {
putLock.unlock();
}
}
队列容量是Integer.MAX_VALUE,将入参collection中的元素都添加到队列中
入队操作
offer(E e)
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;
}
先无锁判断一下,队列是否已经满了(this.count 原子变量,记录当前队列长度)。如果没有满,加put锁,再次检查是否有空间(类似于doublecheck)。如果有空间,那么node入队,count自增。入队后,再判断一下是否还有剩余空间,如果没有,执行notFull.signal()(为put操作放行)
顺便,看一下enqueue方法
private void enqueue(Node<E> node) {
// assert putLock.isHeldByCurrentThread();
// assert last.next == null;
last = last.next = node;
}
链表尾端插入一个node
put(E e)
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
// Note: convention in all put/take/etc is to preset local var
// holding count negative to indicate failure unless set.
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
while (count.get() == capacity) {
notFull.await();
}
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
}
如果队列已满,那么就notFull.await();,释放锁,等其他操作来signal。然后入队。如果队列不满,notFull.signal();如果队列不空,notEmpty.signal();出队操作
- E poll()
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;
}
如果队列长度为空,返回null;然后double check 队列不为空,元素出队,count减1。notEmpty.signal()解除take存在的挂起。如果poll之前的队列长队等于容量,notFull.signal();解除put存在的await
看一下dequeue方法
- E take()
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;
}
和poll相似,只不过是在队列为空的时候,执行了notEmpty.await(),挂起当前线程
其他方法
peek remove都是fullLock