- LinkedBlockingQueue含有一个链表+2把锁+2个条件
- LinkedBlockingQueue的属性和Node节点内部类:
/**
* Linked list node class
*/
static class Node<E> {
E item;
/**
* One of:
* - the real successor Node
* - this Node, meaning the successor is head.next
* - null, meaning there is no successor (this is the last node)
*/
Node<E> next;
Node(E x) { item = x; }
}
/** The capacity bound, or Integer.MAX_VALUE if none
链表容量,最大值 Integer.MAX_VALUE */
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();
- LinkedBlockingQueue重要知识点:
- 创建
- 入队(添加元素)
- 出队(删除元素)
- LinkedBlockingQueue使用方法 (创建):
// 不指定链表容量 默认Integer.MAX_VALUE 可认为是无界队列
LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
对应源码:
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
// 指定链表容量
LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>(5);
对应源码:
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}
LinkedBlockingQueue入队方法:
- public boolean 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) // 队列满了返回false
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(); // 获取旧值并把count + 1
if (c + 1 < capacity) // 比较添加元素后的容量,如果小于总容量,说明还可以添加至少一个元素
notFull.signal(); // 唤醒等待notFull条件的其中一个线程
}
} finally {
putLock.unlock();
}
if (c == 0) // 第一次添加的时候 c 就是count的值 0
signalNotEmpty(); // 通知等待notEmpty条件的线程
return c >= 0;
}
private void enqueue(Node<E> node) {
// assert putLock.isHeldByCurrentThread();
// assert last.next == null;
last = last.next = node;
}
private void signalNotEmpty() {
final ReentrantLock takeLock = this.takeLock;
takeLock.lock(); // 获取出队锁
try {
notEmpty.signal(); // 唤醒等待notEmpty条件的其中一个线程
} finally {
takeLock.unlock();
}
}
- public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException
原理:在队尾插入一个元素,,如果队列已满,则进入等待,直到出现以下三种情况:- 被唤醒
- 等待时间超时
- 当前线程被中断
对应代码:
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;
/*
* 进行等待: 在这个过程中可能发生三件事:
* 1、被唤醒-->继续当前这个while循环
* 2、超时-->继续当前这个while循环 返回false
* 3、被中断-->抛出中断异常InterruptedException
*/
nanos = notFull.awaitNanos(nanos);
}
enqueue(new Node<E>(e)); // 入队
c = count.getAndIncrement();
if (c + 1 < capacity) // 队列没满
notFull.signal(); // 唤醒等到notFull条件的其中一个线程
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
return true;
}
- public void put(E e) throws InterruptedException
原理: 在队尾插入一个元素,如果队列满了,就一直阻塞,直到队列不满了(notFull通知)或者线程被中断
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 {
/*
* 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 (count.get() == capacity) {
notFull.await();
}
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
}
LinkedBlockingQueue出队方法:
- public E poll()
原理: 删除队列头部元素,如果没有元素,直接返回null;如果有元素,返回并出队
public E poll() {
final AtomicInteger count = this.count; // 获取元素数量
if (count.get() == 0) // 没有返回null
return null;
E x = null;
int c = -1;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock(); // 获取出队锁
try {
if (count.get() > 0) { // 元素不为0
x = dequeue(); // 出队
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal(); // 元素个数大于1,即出队之后至少还有一个元素,通知等待noEmpty条件的其中一个线程
}
} finally {
takeLock.unlock();
}
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; // 将头节点的下一个节点赋值给first
h.next = h; // help GC 将当前将要出队的节点置null(为了使其做head节点做准备)
head = first; // 将当前将要出队的节点作为了头节点
E x = first.item; // 获取出队节点的值
first.item = null; // 将出队节点的值置空
return x;
}
private void signalNotFull() {
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
notFull.signal();
} finally {
putLock.unlock();
}
}
- public E poll(long timeout, TimeUnit unit) throws InterruptedException
原理: 删除队列头部元素,如果队列不空,出队;如果队列已空且已经超时,返回null;如果队列已空且时间未超时,则进入等待,直到出现以下三种情况:- 被唤醒
- 等待时间超时
- 当前线程被中断
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) // 超时返回null
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;
}
- public E take() throws InterruptedException
原理: 删除队列头部元素,如果队列空了,一直阻塞,直到队列不为空或者线程被中断
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;
}
-
三种入队对比:
- offer(E e):如果队列没满,立即返回true; 如果队列满了,立即返回false — 不阻塞
- put(E e):如果队列满了,一直阻塞,直到队列不满了或者线程被中断 -->阻塞
- offer(E e, long timeout, TimeUnit unit):在队尾插入一个元素,,如果队列已满,则进入等待,直到出现以下三种情况: -->阻塞
- 被唤醒
- 等待时间超时
- 当前线程被中断
-
三种出队对比:
- poll():如果没有元素,直接返回null;如果有元素,出队 — 不阻塞
- take():如果队列空了,一直阻塞,直到队列不为空或者线程被中断 — 阻塞
- poll(long timeout, TimeUnit unit):如果队列不空,出队;如果队列已空且已经超时,返回null;如果队列已空且时间未超时,则进入等待,直到出现以下三种情况: — 阻塞
- 被唤醒
- 等待时间超时
- 当前线程被中断
扩展信息:
- ArrayBlockingQueue与LinkedBlockingQueue对比
- ArrayBlockingQueue:
一个对象数组+一把锁+两个条件
入队与出队都用同一把锁
在只有入队高并发或出队高并发的情况下,因为操作数组,且不需要扩容,性能很高
采用了数组,必须指定大小,即容量有限 - LinkedBlockingQueue:
一个单向链表+两把锁+两个条件
两把锁,一把用于入队,一把用于出队,有效的避免了入队与出队时使用一把锁带来的竞争。
在入队与出队都高并发的情况下,性能比ArrayBlockingQueue高很多
采用了链表,最大容量为整数最大值,可看做容量无限
- ArrayBlockingQueue: