​https://www.jianshu.com/p/cc2281b1a6bc​

继承关系图

LinkedBlockingQueue_加锁

 

 

 

 

/**
* 节点类,用于存储数据
*/
static class Node<E> {
E item;
Node<E> next;

Node(E x) { item = x; }
}

/**
* 阻塞队列的大小,默认为Integer.MAX_VALUE
*/
private final int capacity;

/**
* 当前阻塞队列中的元素个数
*/
private final AtomicInteger count = new AtomicInteger();

/**
* 阻塞队列的头结点
*/
transient Node<E> head;

/**
* 阻塞队列的尾节点
*/
private transient Node<E> last;

/**
* 获取并移除元素时使用的锁,如take, poll, etc
*/
private final ReentrantLock takeLock = new ReentrantLock();

/**
* notEmpty条件对象,当队列没有数据时用于挂起执行删除的线程
*/
private final Condition notEmpty = takeLock.newCondition();

/**
* 添加元素时使用的锁如 put, offer, etc
*/
private final ReentrantLock putLock = new ReentrantLock();

/**
* notFull条件对象,当队列数据已满时用于挂起执行添加的线程
*/
private final Condition notFull = putLock.newCondition();

这里如果不指定队列的容量大小,也就是使用默认的Integer.MAX_VALUE,如果存在添加速度大于删除速度时候,有可能会内存溢出,这点在使用前希望慎重考虑。

另外,LinkedBlockingQueue对每一个lock锁都提供了一个Condition用来挂起和唤醒其他线程。

默认的构造函数和最后一个构造函数创建的队列大小都为Integer.MAX_VALUE,只有第二个构造函数用户可以指定队列的大小。第二个构造函数最后初始化了last和head节点,让它们都指向了一个元素为null的节点。

LinkedBlockingQueue_构造函数_02


最后一个构造函数使用了putLock来进行加锁,但是这里并不是为了多线程的竞争而加锁,只是为了放入的元素能立即对其他线程可见。

put方法来看,它总共做了以下情况的考虑:

队列已满,阻塞等待。

队列未满,创建一个node节点放入队列中,如果放完以后队列还有剩余空间,继续唤醒下一个添加线程进行添加。如果放之前队列中没有元素,放完以后要唤醒消费线程进行消费。

LinkedBlockingQueue_阻塞队列_03

 

 链表算法

LinkedBlockingQueue_阻塞队列_04

 

 

private E dequeue() {
// 获取到head节点
Node<E> h = head;
// 获取到head节点指向的下一个节点,也就是节点A
Node<E> first = h.next;
// 获取到下下个节点,也就是节点B
Node<E> next = first.next;
// head的next指向下下个节点,也就是图中的B节点
h.next = next;
// 得到节点A的值
E x = first.item;
first.item = null; // help GC
first.next = first; // help GC
return x;
}