LinkedBlockingQueue
LinkedBlockingQueue是一种基于单向链表 的阻塞队列。其队头和队尾分别有两个指针来控制,所以生产者和消费者可以采用独立的锁来控制数据同步,也就意味着在高并发的情况下,生产者和消费者是可以同时操作队列的,从而提高了队列的并发性。
因为生产者和消费者是不同的锁控制的,所以记录队列总数count是一个AtomicInteger的原子变量
1 核心参数
public class LinkedBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
private static final long serialVersionUID = -6903933977591709194L;
/** 队列容量,默认Integer.MAX_VALUE */
private final int capacity;
/** 队列中当前元素的数量 */
private final AtomicInteger count = new AtomicInteger();
/**
* 队列头结点
* Invariant: head.item == null
*/
transient Node<E> head;
/**
* 队列尾结点
* 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();
// 存储元素的Node对象
static class Node<E> {
E item;
Node<E> next;
Node(E x) { item = x; }
}
...........
}
2 构造方法
// 无参构造,默认大小为 Integer.MAX_VALUE
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
/**
* 指定大小创建队列, 头结点和尾结点都为null
* @param capacity 队列长度
*/
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}
/**
* 根据跟定的集合创建一个大小为Integer.MAX_VALUE的队列
* 并且根据集合初始化队列
* @param c 数据集合
*/
public LinkedBlockingQueue(Collection<? extends E> c) {
this(Integer.MAX_VALUE);
final ReentrantLock putLock = this.putLock;
putLock.lock(); // Never contended, but necessary for visibility
.........
}
3 核心方法
添加数据的基本流程如下
-
add(E e)
添加数据,底层调用offer(), 成功返回true, 失败抛出IllegalStateException
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
-
offer(E e)
添加指定数据到队尾,成功返回true, 失败返回 false
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);
// 先获取原始count在+1
c = count.getAndIncrement();
// 如果队列还有剩余空间则通知其他生产者线程
if (c + 1 < capacity)
notFull.signal();
}
} finally {
// 释放锁
putLock.unlock();
}
// 如果原始count=0,说明开始队列是没有数据的
// 添加数据后通知唤醒消费线程来消费数据
if (c == 0)
signalNotEmpty();
return c >= 0;
}
// 把数据放到队列尾部,并且把新增的设置为队尾
private void enqueue(Node<E> node) {
// assert putLock.isHeldByCurrentThread();
// assert last.next == null;
last = last.next = node;
}
-
offer(E e, long timeout, TimeUnit unit)
指定等待时间添加数据,该方法和offer(E e)方法基本一致,但是也有以下的不同之处
- 添加的锁是可中断锁
- 在等待时间内可以被中断
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();
}
// 如果原始count=0,说明开始队列是没有数据的
// 添加数据后通知唤醒消费线程来消费数据
if (c == 0)
signalNotEmpty();
return true;
}
-
put(E e)
添加指定数据到队列,如果队列满了,则阻塞当前线程,直到抛出InterruptedException异常
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();
}
// 如果原始count=0,说明开始队列是没有数据的
// 添加数据后通知唤醒消费线程来消费数据
if (c == 0)
signalNotEmpty();
}
取出数据的基本流程如下
-
poll()
取出队列的头部的元素,获取不到返回null
public E poll() {
final AtomicInteger count = this.count;
// 没有数据直接返回null
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();
// 获得原始数量,并且-1
c = count.getAndDecrement();
// 如果此时队列队列中还有数据,通知其他消费线程
if (c > 1)
notEmpty.signal();
}
} finally {
// 释放锁
takeLock.unlock();
}
// 如果原始count=队列长度,取出来一个后说明队列是非满的
// 取出数据后通知唤醒生产线程来生产数据
if (c == capacity)
signalNotFull();
return x;
}
// 真正取出数据
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;
}
-
poll(long timeout, TimeUnit unit)
取出队列的头部的元素,如果没有元素则等待指定时间直到成功,获取不到返回null
等到过程中断线程则抛出InterruptedExcepution
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;
}
-
take()
取出队列的头部的元素,如果没有元素则一直等待直到成功
等到过程中断线程则抛出InterruptedExcepution
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();
}
// 如果原始count=队列长度,取出来一个后说明队列是非满的
// 取出数据后通知唤醒生产线程来生产数据
if (c == capacity)
signalNotFull();
return x;
}