Java 阻塞队列中的常用方法及区别

    技术2025-05-01  14

    Java 阻塞队列中的常用方法及区别

    文章目录

    Java 阻塞队列中的常用方法及区别前言项目环境1.第一组方法1.1 add 方法1.2 remove 方法1.3 element 方法 2.第二组方法2.1 offer 方法2.2 poll 方法2.3 peek 方法 3.第三组方法3.1 put 方法3.2 take 方法 4.总结5.参考

    前言

    在阻塞队列中有很多方法,它们的功能都非常相似,所以非常有必要对这些类似的方法进行辨析,本章采用分类的方式对阻塞队列中常见的方法进行讨论。

    BlockingQueue 中最常用的和添加、删除相关的 8 个方法,把它们分为三组。这三组方法由于功能很类似,所以比较容易混淆。它们的区别仅在于特殊情况:当队列满了无法添加元素,或者是队列空了无法移除元素时,不同组的方法对于这种特殊情况会有不同的处理方式:

    抛出异常:add、remove、element返回结果但不抛出异常:offer、poll、peek阻塞:put、take

    项目环境

    jdk 1.8github 地址:https://github.com/huajiexiewenfeng/java-concurrent 本章模块:blockingqueue

    1.第一组方法

    1.1 add 方法

    add 方法是往队列里添加一个元素,如果队列满了,就会抛出异常来提示队列已满。示例代码如下:

    public class BlockingQueueMethodsDemo { public static void main(String[] args) { BlockingQueue<String> bQueue = new ArrayBlockingQueue(3); // 添加方法 addMethod(bQueue); } private static void addMethod(BlockingQueue<String> bQueue) { bQueue.add("1"); bQueue.add("2"); bQueue.add("3"); bQueue.add("4"); } }

    执行结果:

    Exception in thread "main" java.lang.IllegalStateException: Queue full at java.util.AbstractQueue.add(AbstractQueue.java:98) at java.util.concurrent.ArrayBlockingQueue.add(ArrayBlockingQueue.java:312) at com.csdn.blockingqueue.BlockingQueueMethodsDemo.addMethod(BlockingQueueMethodsDemo.java:22) at com.csdn.blockingqueue.BlockingQueueMethodsDemo.main(BlockingQueueMethodsDemo.java:15)

    显然在添加第 4 个元素的时候,超过了容量的限制,抛出异常。

    1.2 remove 方法

    remove 方法的作用是删除元素,如果我们删除的队列是空的,由于里面什么都没有,所以也无法删除任何元素,那么 remove 方法就会抛出异常。示例代码如下:

    private static void removeMethod(BlockingQueue<String> bQueue) { bQueue.add("1"); bQueue.add("2"); bQueue.remove(); bQueue.remove(); bQueue.remove(); }

    执行结果:

    Exception in thread "main" java.util.NoSuchElementException at java.util.AbstractQueue.remove(AbstractQueue.java:117) at com.csdn.blockingqueue.BlockingQueueMethodsDemo.removeMethod(BlockingQueueMethodsDemo.java:34) at com.csdn.blockingqueue.BlockingQueueMethodsDemo.main(BlockingQueueMethodsDemo.java:19)

    当调用第三个 remove 方法是,队列中的元素为空,抛出异常。

    1.3 element 方法

    element 方法是返回队列的头部节点,但是并不删除。和 remove 方法一样,如果我们用这个方法去操作一个空队列,想获取队列的头结点,可是由于队列是空的,我们什么都获取不到,会抛出和前面 remove 方法一样的异常:NoSuchElementException。示例代码如下:

    private static void elementMethod(BlockingQueue<String> bQueue) { bQueue.add("1"); bQueue.add("2"); System.out.println("元素:"+bQueue.element()); bQueue.remove(); System.out.println("元素:"+bQueue.element()); bQueue.remove(); System.out.println("元素:"+bQueue.element()); }

    执行结果:

    元素:1 元素:2 Exception in thread "main" java.util.NoSuchElementException at java.util.AbstractQueue.element(AbstractQueue.java:136) at com.csdn.blockingqueue.BlockingQueueMethodsDemo.elementMethod(BlockingQueueMethodsDemo.java:45) at com.csdn.blockingqueue.BlockingQueueMethodsDemo.main(BlockingQueueMethodsDemo.java:20)

    2.第二组方法

    实际上我们通常并不想看到第一组方法抛出的异常,这时我们可以优先采用第二组方法。第二组方法相比于第一组而言要友好一些,当发现队列满了无法添加,或者队列为空无法删除的时候,第二组方法会给一个提示,而不是抛出一个异常。

    2.1 offer 方法

    offer 方法用来插入一个元素,并用返回值来提示插入是否成功。如果添加成功会返回 true,而如果队列已经满了,此时继续调用 offer 方法的话,它不会抛出异常,只会返回一个错误提示:false。示例代码如下:

    private static void offerMethod(BlockingQueue<String> bQueue) { System.out.println(bQueue.offer("1")); System.out.println(bQueue.offer("2")); System.out.println(bQueue.offer("3")); System.out.println(bQueue.offer("4")); System.out.println("队列长度:" + bQueue.size()); }

    执行结果:

    true true true false 队列长度:3

    2.2 poll 方法

    poll 方法和第一组的 remove 方法是对应的,作用也是移除并返回队列的头节点。但是如果当队列里面是空的,没有任何东西可以移除的时候,便会返回 null 作为提示。正因如此,我们是不允许往队列中插入 null 的,否则我们没有办法区分返回的 null 是一个提示还是一个真正的元素。示例代码如下:

    private static void pollMethod(BlockingQueue<String> bQueue) { bQueue.offer("1"); bQueue.offer("2"); System.out.println("元素:" + bQueue.poll()); System.out.println("元素:" + bQueue.poll()); System.out.println("元素:" + bQueue.poll()); }

    执行结果:

    元素:1 元素:2 元素:null

    2.3 peek 方法

    peek 方法和第一组的 element 方法是对应的,意思是返回队列的头元素但并不删除。如果队列里面是空的,它便会返回 null 作为提示。示例代码如下:

    private static void peekMethod(BlockingQueue<String> bQueue) { bQueue.offer("1"); bQueue.offer("2"); System.out.println("元素:" + bQueue.peek()); bQueue.poll(); System.out.println("元素:" + bQueue.peek()); bQueue.poll(); System.out.println("元素:" + bQueue.peek()); }

    执行结果:

    元素:1 元素:2 元素:null

    3.第三组方法

    第三组是阻塞队列最大特色的 put 和 take 方法,这两个方法在 《什么是阻塞队列(BlockingQueue)?》 中讨论过,这里就直接将内容复制过来,加上简单的示例代码。

    3.1 put 方法

    put 方法插入元素时,如果队列没有满,那就和普通的插入一样是正常的插入,但是如果队列已满,那么就无法继续插入,则阻塞,直到队列里有了空闲空间。如果后续队列有了空闲空间,比如消费者消费了一个元素,那么此时队列就会解除阻塞状态,并把需要添加的数据添加到队列中。过程如图所示:

    示例代码:

    private static void putMethod(BlockingQueue<String> bQueue) { try { bQueue.put("1"); bQueue.put("2"); bQueue.put("3"); bQueue.put("4"); } catch (InterruptedException e) { e.printStackTrace(); } }

    当 bQueue.put("4"); 执行的时候,由于队列容量为 3,主线程会一直阻塞。

    3.2 take 方法

    take 方法的功能是获取并移除队列的头结点,通常在队列里有数据的时候是可以正常移除的。可是一旦执行 take 方法的时候,队列里无数据,则阻塞,直到队列里有数据。一旦队列里有数据了,就会立刻解除阻塞状态,并且取到数据。过程如图所示:

    示例代码:

    private static void takeMethod(BlockingQueue<String> bQueue) { try { System.out.println("元素:" +bQueue.take()); } catch (InterruptedException e) { e.printStackTrace(); } }

    由于队列为空,执行之后,主线程会一直阻塞。

    4.总结

    组别方法含义特点第一组add添加一个元素如果队列满,抛出异常 IllegalStateException第一组remove返回并删除队列的头节点如果队列空,抛出异常 NoSuchElementException第一组element返回队列头节点如果队列空,抛出异常 NoSuchElementException第二组offer添加一个元素添加成功,返回 true,添加失败,返回 false第二组poll返回并删除队列的头节点如果队列空,返回 null第二组peek返回队列头节点如果队列空,返回 null第三组put添加一个元素如果队列满,阻塞第三组take返回并删除队列的头节点如果队列空,阻塞

    本文讨论了阻塞队列中常见的 8 个方法,按照各自的特点分为以下三组

    第一组的特点是在无法正常执行的情况下抛出异常第二组的特点是在无法正常执行的情况下不抛出异常,但会用返回值提示运行失败第三组的特点是在遇到特殊情况时让线程陷入阻塞状态,等到可以运行再继续执行

    5.参考

    《Java 并发编程 78 讲》- 徐隆曦
    Processed: 0.012, SQL: 9