LinkedBlockingDequeue 由链表实现双端阻塞队列
可在队首和队尾分别插入和删除操作
可指定队列的容量,若不指定队列容量,默认容量为 Integer.MAX_VALUE
提取码:1l7t
1. LinkedBlockingDeque 使用方式
public class LinkedBlockingDequeDemo {
public static void main(String[] args) throws InterruptedException {
LinkedBlockingDeque<String> buffer = new LinkedBlockingDeque<>(2);
Exchanger stack = new Exchanger(buffer);
Producer p1 = new Producer(stack, "p1");
Consumer c1 = new Consumer(stack, "c1");
p1.start();
c1.start();
Thread.sleep(5000);
p1.setStop(true);
}
}
class Exchanger{
private LinkedBlockingDeque<String> buffer;
public Exchanger(LinkedBlockingDeque<String> buffer){
this.buffer = buffer;
}
public void produce(String message) throws InterruptedException {
buffer.put(message);
}
public String consume() throws InterruptedException {
return buffer.poll(1000, TimeUnit.MILLISECONDS);
}
}
class Producer extends Thread{
private volatile boolean stop;
private AtomicInteger sequence = new AtomicInteger(1);
private Exchanger exchanger;
public Producer(Exchanger exchanger, String name){
super(name);
this.exchanger = exchanger;
}
@Override
public void run() {
while(!stop){
try {
Thread.sleep(500);
String message = String.valueOf((sequence.getAndIncrement()));
this.exchanger.produce(message);
System.out.println(Thread.currentThread().getName() + "---put---" + message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void setStop(boolean stop){
this.stop = stop;
}
}
class Consumer extends Thread{
private volatile boolean stop;
private Exchanger exchanger;
public Consumer(Exchanger exchanger, String name){
super(name);
this.exchanger = exchanger;
}
@Override
public void run() {
try {
Thread.sleep(1000);
String message;
try{
while((message = exchanger.consume()) != null){
System.out.println(Thread.currentThread().getName() + "---take---" + message);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2. LinkedBlockingDeque 属性
// 头节点
transient Node<E> first;
// 尾结点
transient Node<E> last;
// 双端队列中元素数量
private transient int count;
// 双端队列的最大容量
private final int capacity;
// 可重入锁
final ReentrantLock lock = new ReentrantLock();
// 队列非空条件
private final Condition notEmpty = lock.newCondition();
// 队列未满条件
private final Condition notFull = lock.newCondition();
3. LinkedBlockingDeque 构造器
① 无参构造:未指定 capacity ,capacity = Integer.MAX_VALUE
public LinkedBlockingDeque() {
this(Integer.MAX_VALUE);
}
本质:调用指定 capacity = Integer.MAX_VALUE 的有参构造
② 指定容量
public LinkedBlockingDeque(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
}
若指定容量 capacity <= 0 抛出 IllegalArgumentException
异常
③ 指定集合参数
public LinkedBlockingDeque(Collection<? extends E> c) {
this(Integer.MAX_VALUE);
final ReentrantLock lock = this.lock;
lock.lock(); // Never contended, but necessary for visibility
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();
}
}
注意:该构造函数里面使用了 lock,注释部分说,加锁不是因为有竞争,是为了可见度
以集合作为参数,依次将集合元素添加到链表末尾
若集合元素为 null
则抛出NullPointerException
异常
若添加失败,说明队列已满,抛出IllegalStateException("Deque full")
异常
4. LinkedBlockingDeque 入队
4.1 addFirst(E) && addLast(E)
addFirst(E e)
public void addFirst(E e) {
if (!offerFirst(e))
throw new IllegalStateException("Deque full");
}
addLast(E e) 仅需要将其中的 offerFirst(e)
替换为 offerLast(e)
若 offerFirst(E)/offerLast(E)
执行失败,返回 false,抛出 IllegalStateException
异常
4.2 offerFirst(E) && offerLast(E)
offerFirst(E)
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();
}
}
执行流程:
① 判断添加元素E 是否为 null,若为 null 则抛出NullPointerException
异常
② 将元素封装到 Node 结点中
③ ReentrantLock 加锁,调用 linkFirst(node)/linkLast(node)
将node添加到链表中
④ 解锁,返回是否添加成功【添加失败的情况为:队列满】
4.2.1 linkFirst(Node) && linkLast(Node)
linkFirst(node)
private boolean linkFirst(Node<E> node) {
// assert lock.isHeldByCurrentThread();
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;
}
linkLast(node) 仅需要将上述代码中的 first
—last
,prev
—next
互换即可
将元素添加到链表首尾位置的执行流程:
① 判断队列是否满,满则返回 false,添加失败
② 更新首结点,尾结点
③ 判断添加前队列是否为空,为空则更新首尾结点
④ 更新队列大小
⑤ 唤醒等待 非空条件的线程
⑥ 返回操作成功
4.3 putFirst(E) && putLast(E)
putFirst(E e)
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();
}
}
putFirst(E e) 的执行流程同 offerFirst(E e)
不同点:
① 返回值:
offerFirst(E) offerLast(E) 有 boolean 类型的返回值
putFirst(E) putLast(E) 没有返回值
② 阻塞式获取:
offerFirst(E) offerLast(E) 若队列满则返回 false
putFirst(E) putLast(E)调用notFull.await()
无限期等待直到队列非满
相同点:
① 若添加元素为 null,则抛出NullPointerException
异常
② 调用 linkFirst(node) linkLast(node) 添加元素
4.4 offerFirst(E, long, TimeUnit) && offerLast(E, long, TimeUnit)
offerFirst(E e, long timeout, TimeUnit unit)
public boolean offerFirst(E e, long timeout, TimeUnit unit) throws InterruptedException {
if (e == null) throw new NullPointerException();
Node<E> node = new Node<E>(e);
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (!linkFirst(node)) {
if (nanos <= 0)
return false;
nanos = notFull.awaitNanos(nanos);
}
return true;
} finally {
lock.unlock();
}
}
offerFirst(E e, long timeout, TimeUnit unit) 在 offerFirst(E e) 的基础上指定等待时间阻塞式获取
并且设置可中断获取锁:lock.lockInterruptibly()
等待指定时间获取实现流程:
① 通过 linkFirst(node) linkLast(node)
判断是否添加成功
② 添加失败则判断等待是否超时,若不超时则调用notFull.awaitNanos()
继续等待
③ 重复 ① ,②,直到成功添加返回true
,或等待超时返回 false
5. LinkedBlockingDeque 出队
5.1 removeFirst() && removeLast()
removeFirst()
public E removeFirst() {
E x = pollFirst();
if (x == null) throw new NoSuchElementException();
return x;
}
removeFirst() 调用 pollFirst() 返回从队首移出的值x,若x为null,则抛出NoSuchElementException
异常
5.2 pollFirst() && pollLast()
pollFirst()
public E pollFirst() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return unlinkFirst();
} finally {
lock.unlock();
}
}
对比前面,pollFirst() 和 offerFirst(E e) 是相对应的,出队和入队操作
非阻塞式获取,返回是否获取成功
5.2.1 unlinkFirst() && unlinkLast()
unlinkFirst()
private E unlinkFirst() {
Node<E> f = first;
if (f == null)
return null;
Node<E> n = f.next;
E item = f.item;
f.item = null;
f.next = f; // help GC
first = n;
if (n == null)
last = null;
else
n.prev = null;
--count;
notFull.signal();
return item;
}
unlinkLast() 代码部分,只需要将 first – last,next – prev
移除链表首,尾位置上的元素执行流程:
① 判断队列是否为空,为空则返回 null
② 不为空则移除队首,队尾元素
③ 判断移除后队列是否为空,为空则更新首尾结点
④ 更新队列大小
⑤ 唤醒 等待非满条件的 存线程
⑥ 返回 取出的元素值
5.3 takeFirst() && takeLast()
takeFirst()
public E takeFirst() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
E x;
while ( (x = unlinkFirst()) == null)
notEmpty.await();
return x;
} finally {
lock.unlock();
}
}
takeFirst() 和 putFirst(E e) 是相对应的,出队和入队操作 阻塞式获取
putFirst(E e) 若队列满,则调用notFull.await()
无限期等待直到队列非满,由于一定会成功添加元素因此没有返回值
takeFirst() 若队列空,则调用notEmpty.await()
无限期等待直到队列非空,一定会获取成功,并将获取到的值返回
5.4 pollFirst(E, long, TimeUnit) && pollLast(E, long, TimeUnit)
pollFirst(long timeout, TimeUnit unit)
public E pollFirst(long timeout, TimeUnit unit) throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
E x;
while ( (x = unlinkFirst()) == null) {
if (nanos <= 0)
return null;
nanos = notEmpty.awaitNanos(nanos);
}
return x;
} finally {
lock.unlock();
}
}
pollFirst(long timeout, TimeUnit unit) 和 offerFirst(E e, long timeout, TimeUnit unit) 是相对应的
指定时间阻塞式获取和阻塞式添加
若等待超时:
添加失败返回false
否则返回true
获取失败返回null
,否则返回获取到的值
若等待未超时:调用 notFull.awaitNanos
,notEmpty.awaitNanos
继续阻塞式获取
6. FIFO
6.1 in
public boolean add(E e) {
addLast(e);
return true;
}
public boolean offer(E e) {
return offerLast(e);
}
public void put(E e) throws InterruptedException {
putLast(e);
}
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
return offerLast(e, timeout, unit);
}
FIFO 添加时添加到队列的尾部
6.2 out
public E remove() {
return removeFirst();
}
public E poll() {
return pollFirst();
}
public E take() throws InterruptedException {
return takeFirst();
}
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
return pollFirst(timeout, unit);
}
FIFO 删除时移除队列首部