Java LinkedBlockingDeque 双端阻塞队列(多线程、生产者-消费者模式)

介绍

LinkedBlockingDeque 是一个基于 链表 的 双端阻塞队列,线程安全,不允许插入 null 值

与 LinkedBlockingQueue 类似,但两端都可以放数据、取数据

应用场景

一般多用于 生产者消费者模式,详见 链接

构造方法

  1. 创建一个容量为 Integer.MAX_VALUE 的队列:

    LinkedBlockingDeque()
    
  2. 创建一个固定容量的 队列:

    LinkedBlockingDeque(int capacity)
    
  3. 创建一个容量为 Integer.MAX_VALUE 的队列,包含 collection 的元素

    LinkedBlockingDeque(Collection<? extends E> c)
    

方法总结

方法 抛出异常 正常运行 阻塞 阻塞特定时间
尾部添加元素 add(e)、addLast(e) offer(e)、offerLast(e) put(e)、putLast(e) offer(e, time, unit)
头部添加元素 addFirst(e) offerFirst(e) putFirst(e) offerFirst(e, time, unit)
头部取出元素 remove()、removeFirst() poll()、pollFirst() take()、takeFirst() poll(time, unit)、pollFirst(time, unit)
尾部取出元素 removeLast() pollLast() takeLast() pollLast(time, unit)

添加元素

put() 方法

如果队列已满,线程阻塞

例子:

public static void main(String[] args) {
    LinkedBlockingQueue queue = new LinkedBlockingQueue(2);
    try {
        queue.put(1);
        System.out.println("添加第一个元素,队列元素数量:"+queue.size());
        queue.put(2);
        System.out.println("添加第二个元素,队列元素数量:"+queue.size());
        queue.put(3);
        System.out.println("添加第三个元素,队列元素数量:"+queue.size());
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
}

执行结果:

添加第一个元素,队列元素数量:1
添加第二个元素,队列元素数量:2

说明添加第二个元素后,队列已满,线程阻塞,一直等到能够添加该元素

add() 方法

如果队列已满,报下面错误:

java.lang.IllegalStateException: Queue full

例子:

public static void main(String[] args) {
    LinkedBlockingQueue queue = new LinkedBlockingQueue(2);
    queue.add(1);
    System.out.println("添加第一个元素,队列元素数量:"+queue.size());
    queue.add(2);
    System.out.println("添加第二个元素,队列元素数量:"+queue.size());
    queue.add(3);
    System.out.println("添加第三个元素,队列元素数量:"+queue.size());
}

执行结果:

添加第一个元素,队列元素数量:1
添加第二个元素,队列元素数量:2
Exception in thread "main" java.lang.IllegalStateException: Queue full
    at java.util.AbstractQueue.add(AbstractQueue.java:98)
    at LinkedBlockingQueueTest.main(LinkedBlockingQueueTest.java:11)

说明添加第二个元素后,队列已满,就抛错

offer() 方法

如果队列已满,程序正常运行,但是不再新增元素

例子:

public static void main(String[] args) {
    LinkedBlockingQueue queue = new LinkedBlockingQueue(2);
    queue.offer(1);
    System.out.println("添加第一个元素,队列元素:"+queue.size());
    queue.offer(2);
    System.out.println("添加第二个元素,队列元素:"+queue.size());
    queue.offer(3);
    System.out.println("添加第三个元素,队列元素:"+queue.size());
}

执行结果:

添加第一个元素,队列元素:1
添加第二个元素,队列元素:2
添加第三个元素,队列元素:2

取元素

take() 方法

返回并移除第一个元素,当队列为空,线程阻塞

例子-队列为空时:

public static void main(String[] args) {
    LinkedBlockingQueue queue = new LinkedBlockingQueue(2);
    try {
        queue.take();
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
}

执行结果:程序没有执行结束

说明队列没有元素时,一直等待,即:阻塞

例子-队列有元素时

public static void main(String[] args) {
    LinkedBlockingQueue queue = new LinkedBlockingQueue(2);
    queue.add(100);
    queue.add(200);
    System.out.println("队列元素数量:"+queue.size());
    try {
        Object o = queue.take();
        System.out.println("取出第一个元素:"+o);
        System.out.println("队列元素数量:"+queue.size());
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
}

执行结果:

队列元素数量:2
取出第一个元素:100
队列元素数量:1

说明队列有数据时,返回第一个元素,并移除

poll() 方法

返回并移除第一个元素,队列为空时返回 null

例子-队列为空时:

public static void main(String[] args) {
    LinkedBlockingQueue queue = new LinkedBlockingQueue(2);
    Object o = queue.poll();
    System.out.println("取出第一个元素:"+o);
}

执行结果:

取出第一个元素:null

说明队列没有数据时,返回 null

例子-队列有元素时:

public static void main(String[] args) {
    LinkedBlockingQueue queue = new LinkedBlockingQueue(2);
    queue.add(100);
    queue.add(200);
    System.out.println("队列元素数量:"+queue.size());
    Object o = queue.poll();
    System.out.println("取出第一个元素:"+o);
    System.out.println("队列元素数量:"+queue.size());
}

执行结果:

队列元素数量:2
取出第一个元素:100
队列元素数量:1

说明队列有数据时,返回第一个元素,并移除

remove() 方法

返回并移除第一个元素,队列为空时抛异常:java.util.NoSuchElementException

例子-队列为空时:

public static void main(String[] args) {
    LinkedBlockingQueue queue = new LinkedBlockingQueue(2);
    Object o = queue.remove();
    System.out.println("取出第一个元素:"+o);
}

执行结果:

Exception in thread "main" java.util.NoSuchElementException

说明队列没有数据时,会抛错

例子-有元素时:

public static void main(String[] args) {
    LinkedBlockingQueue queue = new LinkedBlockingQueue(2);
    queue.add(100);
    queue.add(200);
    System.out.println("队列元素数量:"+queue.size());
    Object o = queue.remove();
    System.out.println("取出第一个元素:"+o);
    System.out.println("队列元素数量:"+queue.size());
}

执行结果:

队列元素数量:2
取出第一个元素:100
队列元素数量:1

说明队列有数据时,返回第一个元素,并移除

其他方法

remainingCapacity():获取队列中剩余的空间

size():获得队列中有多少值

应用场景

应用在 生产者/消费者模式

生产者

package 生产者模式;

import java.util.concurrent.LinkedBlockingQueue;

public class Productor implements Runnable {
    int index=1;
    LinkedBlockingQueue queue =null;

    public Productor(LinkedBlockingQueue queue){
        this.queue =queue;
    }
    @Override
    public void run() {
        while (true) {
            if (queue.size() == Main.QUEUE_MAX) {
                System.out.println("队列的馒头数量为:"+ queue.size()+",达到上限,不在生产");
            }
            String str = "馒头-编号:" + index;
            try {
                queue.put(str);
                System.out.println("生产一个 " + str);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            index++;

//                模拟生产馒头,生产馒头慢,暂停200毫秒
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
}

消费者

package 生产者模式;

import java.util.concurrent.LinkedBlockingQueue;

public class Customer implements Runnable {

    LinkedBlockingQueue queue =null;

    public Customer(LinkedBlockingQueue queue){
        this.queue =queue;
    }
    @Override
    public void run() {
        while (true) {
            if (queue.size() == 0) {
                System.out.println("队列的馒头数量为:0,暂停营业");
            }
            try {
                System.out.println("卖出 " + queue.take());
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
//              模拟卖馒头,卖馒头快,暂停20毫秒
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

主启动类

package 生产者模式;

import java.util.concurrent.LinkedBlockingQueue;

public class Main {
    final static int QUEUE_MAX = 5;

    public static void main(String[] args) {
        LinkedBlockingQueue queue=new LinkedBlockingQueue(QUEUE_MAX);
        Productor p=new Productor(queue);
        Customer c=new Customer(queue);

        Thread tp=new Thread(p);
        Thread tc=new Thread(c);


        tc.start();

        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        tp.start();

    }
}

执行结果:

队列的馒头数量为:0,暂停营业
生产一个 馒头-编号:1            // 生成一个馒头
卖出 馒头-编号:1            // 卖出一个馒头
生产一个 馒头-编号:2            // 生成馒头快,连续生成5个馒头
生产一个 馒头-编号:3
生产一个 馒头-编号:4
生产一个 馒头-编号:5
生产一个 馒头-编号:6
队列的馒头数量为:5,达到上限,不在生产        // 队列已满,馒头放不进去,阻塞生产者线程
卖出 馒头-编号:2            // 消费者线程时间到,卖出一个馒头(该馒头是第二个馒头),此时队列空出一个位置
生产一个 馒头-编号:7            // 队列空出一个位置,可以放进去一个馒头,队列又满了
队列的馒头数量为:5,达到上限,不在生产 // 队列已满,馒头放不进去,阻塞生产者线程
卖出 馒头-编号:3
生产一个 馒头-编号:8
队列的馒头数量为:5,达到上限,不在生产
卖出 馒头-编号:4
生产一个 馒头-编号:9

参考:

https://juejin.cn/post/7000515796053000228
https://blog.csdn.net/baofengyu90/article/details/84564013
https://blog.csdn.net/qq_24614847/article/details/109443062


原文出处:/show_1IX64Apm5V4K.html