Java Interview in Action - LinkedBlockingDeque

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) 仅需要将上述代码中的 firstlastprevnext互换即可
在这里插入图片描述
将元素添加到链表首尾位置的执行流程:

① 判断队列是否满,满则返回 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.awaitNanosnotEmpty.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 删除时移除队列首部

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值