【JDK源码分析系列】LinkedBlockingQueue源码分析
【1】LinkedBlockingQueue 继承体系图示
【2】LinkedBlockingQueue 源码分析
【2.1】LinkedBlockingQueue 主要属性
链表结构相关属性
public class LinkedBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
// 链表的作用是为了保存当前节点,节点中的数据可以是任意东西,
// 若队列被应用到线程池时,节点就是线程
// 若队列被应用到消息队列中,节点就是消息
// 节点的含义主要看队列被使用的场景
//
// 链表节点
static class Node<E> {
// 链表节点中的数据元素
E item;
// 当前元素的下一个,为空表示当前节点是最后一个
Node<E> next;
// 节点构造函数
Node(E x) { item = x; }
}
// 链表的容量,默认Integer.MAX_VALUE
private final int capacity;
// 链表已有元素大小,使用 AtomicInteger 线程安全
private final AtomicInteger count = new AtomicInteger();
// 链表头
transient Node<E> head;
// 链表尾
private transient Node<E> last;
}
锁相关属性
public class LinkedBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
// 设计两把锁的目的使得 take 和 put 可以同时进行
// 取元素(take)时的锁
private final ReentrantLock takeLock = new ReentrantLock();
// 取元素(take)的条件队列
private final Condition notEmpty = takeLock.newCondition();
// 放元素(put)时的锁
private final ReentrantLock putLock = new ReentrantLock();
// 放元素(put)的条件队列
private final Condition notFull = putLock.newCondition();
}
【2.2】LinkedBlockingQueue 构造函数
public class LinkedBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
// 不指定容量,默认 Integer 的最大值
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
// 指定链表容量大小,链表头尾相等,节点值(item)都是 null
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}
// 已有集合数据进行初始化
public LinkedBlockingQueue(Collection<? extends E> c) {
// 默认初始化容量Integer.MAX_VALUE
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();
// capacity 代表链表的大小,在这里是 Integer 的最大值
// 如果集合类的大小大于 Integer 的最大值,就会报错
// 此处判断可以放在循环外部
// 若给定集合大小是 1w,链表大小是 9k,
// 按照现在代码实现,只能在 for 循环 9k 次时才能发现,
// 则原来给定集合的大小已经大于链表大小,会导致 9k 次循环都是在浪费资源
if (n == capacity)
throw new IllegalStateException("Queue full");
// 入队,把新元素放到队尾
enqueue(new Node<E>(e));
++n;
}
// 链表已有元素大小,使用 AtomicInteger 线程安全
count.set(n);
} finally {
putLock.unlock();
}
}
}
【2.3】LinkedBlockingQueue 添加元素
public class LinkedBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
public boolean offer(E e) {
// 检查元素是否为null
if (e == null) throw new NullPointerException();
// 获取队列中已有的元素数量
final AtomicInteger count = this.count;
// 队列满了则直接返回
if (count.get() == capacity)
return false;
// 预先设置c为-1,约定负数为新增失败
int c = -1;
// 新建节点
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
if (count.get() < capacity) {
// 若队列未满则入队操作
enqueue(node);
// 新增计数赋值,注意这里 getAndIncrement 返回的是旧值
// 这里的 c 是比真实的 count 小 1 的
c = count.getAndIncrement();
// 如果链表现在的大小小于链表的容量,说明队列未满
// 可以尝试唤醒一个 notFull 的等待线程
if (c + 1 < capacity)
notFull.signal();
}
} finally {
putLock.unlock();
}
if (c == 0)
// c == 0,表示队列里有一个元素
// 会尝试唤醒一个取元素(take)的等待线程
signalNotEmpty();
return c >= 0;
}
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
// e 为空,抛出异常
if (e == null) throw new NullPointerException();
// 记录 timeout 时长
long nanos = unit.toNanos(timeout);
// 预先设置c为-1,约定负数为新增失败
int c = -1;
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
// 设置可中断锁,加锁若线程中断则抛出异常
putLock.lockInterruptibly();
try {
// 队列满了
// 会阻塞一段时间后,继续运行
while (count.get() == capacity) {
// 如果当前等待的时间小于等于 0 ,说明已经超过等待时间了
// 直接返回 false
if (nanos <= 0)
return false;
// 阻塞等待 nanos 时间,如果超过这段时间,返回的值小于等于 0
nanos = notFull.awaitNanos(nanos);
}
// 队列没有满,直接新增到队列的尾部
enqueue(new Node<E>(e));
// 新增计数赋值,注意这里 getAndIncrement 返回的是旧值
// 这里的 c 是比真实的 count 小 1 的
c = count.getAndIncrement();
// 如果链表现在的大小小于链表的容量,说明队列未满
// 可以尝试唤醒一个 notFull 的等待线程
if (c + 1 < capacity)
notFull.signal();
} finally {
// 释放锁
putLock.unlock();
}
if (c == 0)
// c == 0,表示队列里有一个元素
// 会尝试唤醒一个取元素(take)的等待线程
signalNotEmpty();
return true;
}
// 把e新增到队列的尾部
// 如果有可以新增的空间的话,直接新增成功,否则当前线程陷入等待
public void put(E e) throws InterruptedException {
// e 为空,抛出异常
if (e == null) throw new NullPointerException();
// 预先设置c为-1,约定负数为新增失败
int c = -1;
// 新建节点
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
// 设置可中断锁,加锁线程中断则抛出异常
putLock.lockInterruptibly();
try {
// 队列满了
// 当前线程阻塞,等待其他线程的唤醒
// 其他线程 take 成功后就会唤醒此处被阻塞的线程
while (count.get() == capacity) {
// 当队列满的时候等待在 notFull 条件上
notFull.await();
}
// 队列没有满,直接新增到队列的尾部
enqueue(node);
// 新增计数赋值,注意这里 getAndIncrement 返回的是旧值
// 这里的 c 是比真实的 count 小 1 的
c = count.getAndIncrement();
// 如果链表现在的大小小于链表的容量,说明队列未满
// 可以尝试唤醒一个 put 的等待线程
if (c + 1 < capacity)
notFull.signal();
} finally {
// 释放锁
putLock.unlock();
}
// c==0,代表队列里面有一个元素
// 会尝试唤醒一个取元素(take)的等待线程
if (c == 0)
signalNotEmpty();
}
// 入队,把新元素放到队尾
private void enqueue(Node<E> node) {
last = last.next = node;
}
}
【2.4】LinkedBlockingQueue 获取元素
public class LinkedBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
public E poll() {
// count 代表当前链表数据的真实大小
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();
// 减一计算,注意 getAndDecrement 返回的值是旧值
// c 比真实的 count 大 1
c = count.getAndDecrement();
// 如果队列里面有值,从取元素(take)的等待线程里面唤醒一个线程
if (c > 1)
// 唤醒notEmpty条件等待的线程
notEmpty.signal();
}
} finally {
takeLock.unlock();
}
// 如果队列空闲还剩下一个,尝试从放元素(put)的等待线程中唤醒一个线程
if (c == capacity)
signalNotFull();
return x;
}
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
E x = null;
// 默认负数,代表失败
int c = -1;
// 获取timeout
long nanos = unit.toNanos(timeout);
// count 代表当前链表数据的真实大小
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
// 设置可中断锁,加锁线程中断则抛出异常
takeLock.lockInterruptibly();
try {
// 空队列时,阻塞,等待其他线程唤醒
while (count.get() == 0) {
// 如果当前等待的时间小于等于 0 ,说明已经超过等待时间了
// 直接返回 false
if (nanos <= 0)
return null;
// 阻塞等待 nanos 时间,如果超过这段时间,返回的值小于等于 0
nanos = notEmpty.awaitNanos(nanos);
}
// 非空队列,从队列的头部拿一个出来
x = dequeue();
// 减一计算,注意 getAndDecrement 返回的值是旧值
// c 比真实的 count 大 1
c = count.getAndDecrement();
// 如果队列里面有值,从取元素(take)的等待线程里面唤醒一个线程
if (c > 1)
// 唤醒notEmpty条件等待的线程
notEmpty.signal();
} finally {
takeLock.unlock();
}
// 如果队列空闲还剩下一个,尝试从放元素(put)的等待线程中唤醒一个线程
if (c == capacity)
signalNotFull();
return x;
}
// 阻塞拿数据
public E take() throws InterruptedException {
E x;
// 默认负数,代表失败
int c = -1;
// count 代表当前链表数据的真实大小
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
// 设置可中断锁,加锁线程中断则抛出异常
takeLock.lockInterruptibly();
try {
// 空队列时,阻塞,等待其他线程唤醒
while (count.get() == 0) {
// 等待在notEmpty条件
notEmpty.await();
}
// 非空队列,从队列的头部拿一个出来
x = dequeue();
// 减一计算,注意 getAndDecrement 返回的值是旧值
// c 比真实的 count 大 1
c = count.getAndDecrement();
// 如果队列里面有值,从取元素(take)的等待线程里面唤醒一个线程
if (c > 1)
// 唤醒notEmpty条件等待的线程
notEmpty.signal();
} finally {
// 释放锁
takeLock.unlock();
}
// 如果队列空闲还剩下一个,尝试从放元素(put)的等待线程中唤醒一个线程
if (c == capacity)
signalNotFull();
return x;
}
// 队头中取数据
// 链表头节点不存储数据
private E dequeue() {
// 取出头节点
Node<E> h = head;
// 头节点的下一个节点为 first
Node<E> first = h.next;
// 使 h 的下一个节点指向自己
h.next = h; // help GC
// 给链表头赋值
head = first;
// 取出链表头值
E x = first.item;
// 旧头节点指向 null,帮助 GC
first.item = null;
// 返回旧头节点值
return x;
}
}
【2.5】LinkedBlockingQueue 查看元素
public class LinkedBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
// 查看并不删除元素,如果队列为空,返回 null
public E peek() {
// count 代表队列实际大小,队列为空,直接返回 null
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();
}
}
}
致谢
本博客为博主的学习实践总结,并参考了众多博主的博文,在此表示感谢,博主若有不足之处,请批评指正。
【1】面试官系统精讲Java源码及大厂真题