java LinkedBlockingDeque详解
LinkedBlockingDeque介绍
【1】LinkedBlockingDeque是一个基于链表实现的双向阻塞队列,默认情况下,该阻塞队列的大小为Integer.MAX_VALUE,可以看做无界队列,但也可以设置容量限制,作为有界队列。
【2】相比于其他阻塞队列,LinkedBlockingDeque 多了 addFirst、addLast、peekFirst、peekLast 等方法。以first结尾的方法,表示插入、获取或移除双端队列的第一个元素。以 last 结尾的方法,表示插入、获取或移除双端队列的最后一个元素。但本质上并没有优化锁的竞争情况,因为不管是从队首还是队尾,都是在竞争同一把锁,只不过数据插入和获取的方式多了。
LinkedBlockingDeque的源码分析
【1】属性值
//典型的双端链表结构
static final class Node<E> {
E item; //存储元素
Node<E> prev; //前驱节点
Node<E> next; //后继节点
Node(E x) {
item = x;
}
}
// 链表头 本身是不存储任何元素的,初始化时item指向null
transient Node<E> first;
// 链表尾
transient Node<E> last;
// 元素数量
private transient int count;
// 容量,指定容量就是有界队列
private final int capacity;
//重入锁
final ReentrantLock lock = new ReentrantLock();
// 当队列无元素时,锁会阻塞在notEmpty条件上,等待其它线程唤醒
private final Condition notEmpty = lock.newCondition();
// 当队列满了时,锁会阻塞在notFull上,等待其它线程唤醒
private final Condition notFull = lock.newCondition();
【2】构造函数
public LinkedBlockingDeque() {
// 如果没传容量,就使用最大int值初始化其容量
this(Integer.MAX_VALUE);
}
public LinkedBlockingDeque(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
}
public LinkedBlockingDeque(Collection<? extends E> c) {
this(Integer.MAX_VALUE);
final ReentrantLock lock = this.lock;
lock.lock(); // 为保证可见性而加的锁
try {
for (E e : c) {
if (e == null)
throw new NullPointerException();
//从尾部插入元素,插入失败抛出异常
if (!linkLast(new Node<E>(e)))
throw new IllegalStateException("Deque full");
}
} finally {
lock.unlock();
}
}
【3】核心方法分析
1)入队方法
//添加头结点元素
public void addFirst(E e) {
//如果添加失败,抛出异常
if (!offerFirst(e))
throw new IllegalStateException("Deque full");
}
//添加尾结点元素
public void addLast(E e) {
//如果添加失败,抛出异常
if (!offerLast(e))
throw new IllegalStateException("Deque full");
}
//添加头结点元素
public boolean offerFirst(E e) {
//添加的元素为空 抛出空指针异常
if (e == null) throw new NullPointerException();
//将元素构造为结点
Node<E> node = new Node<E>(e);
//这边会加锁,并调用添加头结点插入的核心方法
final ReentrantLock lock = this.lock;
lock.lock();
try {
return linkFirst(node);
} finally {
//解锁
lock.unlock();
}
}
//添加尾结点元素
public boolean offerLast(E e) {
//添加的元素为空 抛出空指针异常
if (e == null) throw new NullPointerException();
//将元素构造为结点
Node<E> node = new Node<E>(e);
//这边会加锁,并调用添加尾结点插入的核心方法
final ReentrantLock lock = this.lock;
lock.lock();
try {
return linkLast(node);
} finally {
lock.unlock();
}
}
//头部插入
private boolean linkFirst(Node<E> node) {
//当前容量大于队列最大容量时,直接返回插入失败
if (count >= capacity)
return false;
//队列中的头结点
Node<E> f = first;
//原来的头结点作为 新插入结点的后一个结点
node.next = f;
//替换头结点 为新插入的结点
first = node;
//尾结点不存在,将尾结点置为当前新插入的结点
if (last == null)
last = node;
else
//原来的头结点的上一个结点为当前新插入的结点
f.prev = node;
//当前容量增加
++count;
//唤醒读取时因队列中无元素而导致阻塞的线程
notEmpty.signal();
return true;
}
//尾部插入
private boolean linkLast(Node<E> node) {
//当前容量大于队列最大容量时,直接返回插入失败
if (count >= capacity)
return false;
//获取尾节点
Node<E> l = last;
//将新插入的前一个节点指向原来的尾节点
node.prev = l;
//尾结点设置为新插入的结点
last = node;
//头结点为空,新插入的结点作为头节点
if (first == null)
first = node;
else
//将原尾结点的下一个结点指向新插入的节点
l.next = node;
//当前容量增加
++count;
//唤醒读取时因队列中无元素而导致阻塞的线程
notEmpty.signal();
return true;
}
//头结点插入
public void putFirst(E e) throws InterruptedException {
//元素不能为空
if (e == null) throw new NullPointerException();
//将元素构造为结点
Node<E> node = new Node<E>(e);
//这边会加锁,并调用添加头结点插入的核心方法
final ReentrantLock lock = this.lock;
lock.lock();
try {
//头结点如果插入失败,会阻塞该方法,直到取出结点或删除结点时被唤醒
while (!linkFirst(node))
notFull.await();
} finally {
//解锁
lock.unlock();
}
}
//尾结点插入
public void putLast(E e) throws InterruptedException {
//元素不能为空
if (e == null) throw new NullPointerException();
//将元素构造为结点
Node<E> node = new Node<E>(e);
//这边会加锁,并调用添加尾结点插入的核心方法
final ReentrantLock lock = this.lock;
lock.lock();
try {
//尾结点如果插入失败,会阻塞该方法,直到取出结点或删除结点时被唤醒
while (!linkLast(node))
notFull.await();
} finally {
lock.unlock();
}
}
//头结点插入 可指定阻塞时间
public boolean offerFirst(E e, long timeout, TimeUnit unit)
throws InterruptedException {
//元素不能为空
if (e == null) throw new NullPointerException();
//将元素构造为结点
Node<E> node = new Node<E>(e);