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

介绍

LinkedBlockingQueue是一个基于 链表 的 FIFO无界阻塞队列,不允许插入 null 值

解释:

  • FIFO:先进先出
  • 无界:没有大小限制
  • 阻塞:数据满了,再添加数据时,会阻塞线程;没有数据时,再取出数据,也会阻塞线程

与 ArrayBlockingQueue 对比

吞吐量通常要高于 ArrayBlockingQueue

应用场景

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

构造方法

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

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

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

    LinkedBlockingQueue(Collection<? extends E> c)
    

FIFO先进先出

队列的头部元素是入队时间最长的元素,队列的尾部元素是在入队时间最短的元素,队列执行获取操作会获得位于队列头部的元素,而新元素会被插入到队列的尾部

方法总结

方法 抛出异常 正常运行 阻塞 阻塞特定时间
入队 add(e) offer(e) put(e) offer(e, time, unit)
出队 remove() poll() take() poll(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_1IX63oihnbGH.html