- 基于链表实现,线程安全的阻塞队列。
- “two lock queue”算法变体,双锁(ReentrantLock):takeLock、putLock,允许读写并行,remove(e)和迭代器iterators需要获取2个锁。
- FIFO先进先出模式。
- 在大部分并发场景下,LinkedBlockingQueue的吞吐量比ArrayBlockingQueue更好。
- 队列头head是存在于队列中最久的元素,队尾tail是最新加入队列的元素。
- 每次插入操作都将动态构造Linked nodes。
- publicclass LinkedBlockingQueue<E>extendsAbstractQueue<E>implements BlockingQueue<E>, java.io.Serializable。
- AbstractQueue:提供Queue必须实现的方法,add(e)、remove()、element()、clear()、addAll(c)。
- private final intcapacity:队列容量,创建时指定,默认Integer.MAX_VALUE。
- private final AtomicIntegercount= new AtomicInteger():队列实际元素个数,size()方法返回count值。
- 【重要】private finalReentrantLocktakeLock= new ReentrantLock():控制取数据的锁;putLock:控制存放数据的锁。
- 【重要】private finalConditionnotEmpty= takeLock.newCondition():出队条件(takeLock锁的条件),队列为空时,通过此Condition让获取元素的线程等待;notFull:入队条件(putLock)。
- static class Node<E>:链表Node节点
static class Node<E> {
E item; // 数据
Node<E> next; // 下一个节点的指针,默认为null意味为最后一个节点,无后继
Node(E x) { item = x; }
}
- LinkedBlockingQueue():初始化容量为Integer.MAX_VALUE的队列;
- LinkedBlockingQueue(int capacity):指定队列容量并初始化头尾节点,if (capacity <= 0) throw new IllegalArgumentException();last = head = new Node<E>(null)。
- LinkedBlockingQueue(Collection<? extends E> c):初始化一个容量为Integer.MAX_VALUE且包含集合c所有元素的队列,且阻塞队列的迭代顺序同集合c。若集合c元素包含null,将throwNullPointerException;若集合c元素个数达到Integer.MAX_VALUE,将throwIllegalStateException("Queue full")。
// 将node链接到队列尾部
private void enqueue(Node<E> node) { // 入队
// assert putLock.isHeldByCurrentThread();
// assert last.next == null;
last = last.next = node; // 等价于last.next = node;last = last.next(即node)
}
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)); // 执行last = last.next = node;
++n;
}
count.set(n); // 设置队列元素个数
} finally {
putLock.unlock();
}
}
/**
* 唤醒notEmpty上正在等待获取元素的线程
*/
private void signalNotEmpty() {
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
notEmpty.signal();
} finally {
takeLock.unlock();
}
}
// 队列已满: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(); // 获取插入锁putLock
try {
if (count.get() < capacity) { // 加锁后再次判断队列是否已满
enqueue(node); // 入队
c = count.getAndIncrement(); // 返回Inc之前的值
if (c + 1 < capacity) // 插入节点后队列未满
notFull.signal(); // 唤醒notFull上的等待线程
}
} finally {
putLock.unlock(); // 释放插入锁
}
if (c == 0)
signalNotEmpty(); // 如果offer前队列为空,则唤醒notEmpty上的等待线程
return c >= 0;
}
public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException {}
long nanos = unit.toNanos(timeout); //将指定的时间长度转换为毫秒来进行处理
while (count.get() == capacity) {
if (nanos <= 0) // 等待的剩余时间小于等于0,那么直接返回false
return false;
nanos = notFull.awaitNanos(nanos); // 最多等待时间(纳秒)
}
long awaitNanos(long nanosTimeout) throws InterruptedException;
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();
// 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;
// 可中断的锁获取操作(优先考虑响应中断),如果线程由于获取锁而处于Blocked状态时,线程将被中断而不再继续等待(throws InterruptedException),可避免死锁。
putLock.lockInterruptibly();
try {
/*
* Note that count is used in wait guard even though it is
* not protected by lock. This works because count can
* only decrease at this point (all other puts are shut
* out by lock), and we (or some other waiting put) are
* signalled if it ever changes from capacity. Similarly
* for all other uses of count in other wait guards.
*/
// 队列若满线程将处于等待状态。while循环可避免“伪唤醒”(线程被唤醒时队列大小依旧达到最大值)
while (count.get() == capacity) {
notFull.await(); // notFull:入队条件
}
enqueue(node); // 将node链接到队列尾部
c = count.getAndIncrement(); // 元素入队后队列元素总和
if (c + 1 < capacity) // 队列未满
notFull.signal(); // 唤醒其他执行入队列的线程
} finally {
putLock.unlock(); // 释放锁
}
// c=0说明队列之前为空,出队列线程均处于等待状态。添加一个元素后,队列已不为空,于是唤醒等待获取元素的线程
if (c == 0)
signalNotEmpty();
}
/**
* 唤醒等待插入数据的线程. Called only from take/poll.
*/
private void signalNotFull() {
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
notFull.signal();
} finally {
putLock.unlock();
}
}
/**
* 队列头部元素出队.
*
* @return the node
*/
private E dequeue() {
// assert takeLock.isHeldByCurrentThread();
// assert head.item == null;
Node<E> h = head; // 临时变量h
Node<E> first = h.next;
h.next = h; // 形成环引用help GC
head = first;
E x = first.item;
first.item = null;
return x;
}
// 队列为空返回null而不是抛异常
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(); // 减1并返回旧值
if (c > 1)
notEmpty.signal(); // 唤醒其他取数据的线程
}
} finally {
takeLock.unlock();
}
// c等于capacity说明poll之前队列已满,poll一个元素后便可唤醒其他等待插入数据的线程
if (c == capacity)
signalNotFull();
return x;
}
public E poll(long timeout, TimeUnit unit)throws InterruptedException{}
4.2、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) // take数据前队列大小大于1,则take后队列至少还有1个元素
notEmpty.signal(); // 唤醒其他取数据的线程
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull(); //唤醒其他等待插入数据的线程
return x;
}
public int drainTo(Collection<? super E> c, int maxElements) {
if (c == null)
throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
if (maxElements <= 0)
return 0;
boolean signalNotFull = false;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
int n = Math.min(maxElements, count.get());//转移元素数量不能超过队列总量
// count.get provides visibility to first n Nodes
Node<E> h = head;
int i = 0;
try {
while (i < n) {
Node<E> p = h.next;//从队首获取元素
c.add(p.item);
p.item = null;//p为临时变量,置null方便GC
h.next = h;
h = p;
++i;
}
return n;
} finally {
// Restore invariants even if c.add() threw
if (i > 0) { // 有数据被转移到集合c中
// assert h.item == null;
head = h;
//如果转移前的队列大小等于队列容量,则说明现在队列未满
// 更新count为队列实际大小(减去i得到)
signalNotFull = (count.getAndAdd(-i) == capacity);
}
}
} finally {
takeLock.unlock();
if (signalNotFull)
signalNotFull(); // 唤醒其他等待插入数据的线程
}
}
public int drainTo(Collection<? super E> c) {
return drainTo(c, Integer.MAX_VALUE);
}
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c); // 集合为null则throw NPE
boolean modified = false;
Iterator<E> it = iterator();
while (it.hasNext()) {
if (!c.contains(it.next())) {
it.remove();
modified = true; // 队列因此请求而改变则返回true
}
}
return modified;
}
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();
}
}
/**
* Locks to prevent both puts and takes.
*/
void fullyLock() {
putLock.lock();
takeLock.lock();
}
/**
* Unlocks to allow both puts and takes.
*/
void fullyUnlock() {
takeLock.unlock();
putLock.unlock();
}
欢迎个人转载,但须在文章页面明显位置给出原文连接;
未经作者同意必须保留此段声明、不得随意修改原文、不得用于商业用途,否则保留追究法律责任的权利。
【 CSDN 】:csdn.zxiaofan.com
【GitHub】:github.zxiaofan.com
如有任何问题,欢迎留言。祝君好运!
Life is all about choices!
将来的你一定会感激现在拼命的自己!