1.LinkedBlockingQueue实现原理:源码分析如下
public class LinkedBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
private static final long serialVersionUID = -6903933977591709194L;
static class Node<E> {
E item; // 当前node(节点) 存放的值
Node<E> next; // 指向下一个node节点
Node(E x) { item = x; }
}
/** LinkedBlockingQueue队列的容量值,默认值为Integer.MAX_VALUE */
private final int capacity;
/** 当前元素记录数*/
private final AtomicInteger count = new AtomicInteger(0);
/**
* 头节点
* 头节点元素值为null(head.item == null)
*/
private transient Node<E> head;
/**
* 最后一个节点.
* 节点的下一个元素为null(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构造方法
* 容量大小为:Integer.MAX_VALUE
*/
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
/**
* LinkedBlockingQueue构造方法
* @param capacity 指定队列容量
*/
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException(); // 如果新添的元素为null,抛错
int c = -1;
Node<E> node = new Node(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(); // 当前元素+1,并将+1的前的值返回
if (c + 1 < capacity) // 如果当前容器的元素计数器个数小于队列的最大容量
notFull.signal(); // 通知生产者,可继续生产加入元素,唤醒notFull.await();等待的线程
} finally {
putLock.unlock(); // 释放锁
}
if (c == 0) // 如果当前队列容量计数器为0(如:第一次添加元素)
signalNotEmpty();
}
/**
* 将最后一个节点元素指向新建的节点元素
* @param node
*/
private void enqueue(Node<E> node) {
last = last.next = node;
}
/**
* 唤醒notEmpty.await();等待的线程:如当前容器为0时,调用了 take()方法的线程
*/
private void signalNotEmpty() {
final ReentrantLock takeLock = this.takeLock; // 拿锁
takeLock.lock(); // 尝试获取锁
try {
notEmpty.signal(); // 唤醒notEmpty.await();等待的线程
} finally {
takeLock.unlock(); // 释放锁
}
}
/**
* 取出第一个节点元素的值(头节点的下一个节点)
*/
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(); // 当前容器计数器-1,并返回-1之前的数值
if (c > 1) // 如果当前元素记录器值大于1,则唤醒消费则 notEmpty.await();
notEmpty.signal(); // C>1 表示当前容器个数已为0
} finally {
takeLock.unlock(); // 释放锁
}
if (c == capacity) // 如果当前容器容量计数器的个数等于队列的最大容量,此时实际容量已小于最大容量,可生产
signalNotFull(); // 唤醒put(E e)方法 调用 notFull.await();阻塞的线程(唤醒生产者开始生产)
return x;
}
/**
* 取出第一个节点(头节点的下一个节点)的元素值,
* 并将头节点的下一个节点的元素值置为null
* 然后将头节点指向原来头节点的下一个节点
* @return
*/
private E dequeue() {
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;
}
/**
* 唤醒生产则开始生产
*/
private void signalNotFull() {
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
notFull.signal(); // 唤醒put(E e)方法 调用 notFull.await();阻塞的线程(唤醒生产者开始生产)
} finally {
putLock.unlock();
}
}
1.创建实例对象,调用LinkedBlockingQueue(int capacity)方法,内存图:
2.调用put(E e)方法里的 enqueue(Node<E> node) 方法中的 last.next = node; 内存图
3.调用put(E e)方法里的 enqueue(Node<E> node) 方法中的 last = last.next = node;内存图:
4.调用put(E e)方法里的 enqueue(Node<E> node) 继续添加下一个元素,last.next = node; 内存图
5..调用put(E e)方法里的 enqueue(Node<E> node) 继续添加下一个元素,last = last.next = node;内存图:
6.删除一个元素:调用E take()方法里的 E dequeue()方法 里的 Node<E> h = head; 内存图:
7.删除一个元素:调用E take()方法里的 E dequeue()方法 里的 Node<E> first = h.next; 内存图:
8.删除一个元素:调用E take()方法里的 E dequeue()方法 里的 h.next = h; 内存图:
9.删除一个元素:调用E take()方法里的 E dequeue()方法 里的 head = first; 内存图:
10.删除一个元素:调用E take()方法里的 E dequeue()方法 里的 first.item = null; 内存图:
总结:LinkedBlockingQueue链表是基于单项链表结构的队列,按照先进先出(FIFO)的原则取出数据。使用了两个重入锁来维护链表的添加和删除节点。
参考: 点击打开链接