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

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

前言

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

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

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

项目环境

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

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 方法的时候,队列里无数据,则阻塞,直到队列里有数据。一旦队列里有数据了,就会立刻解除阻塞状态,并且取到数据。过程如图所示:

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 个方法,按照各自的特点分为以下三组

  • 第一组的特点是在无法正常执行的情况下抛出异常
  • 第二组的特点是在无法正常执行的情况下不抛出异常,但会用返回值提示运行失败
  • 第三组的特点是在遇到特殊情况时让线程陷入阻塞状态,等到可以运行再继续执行
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java阻塞队列是一种线程安全的数据结构,它提供了同步的功能,用于在多线程环境安全地进行数据交换和通信。其实现原理主要涉及以下几个方面。 首先,阻塞队列的实现会使用锁机制确保线程安全。Java可以使用ReentrantLock或synchronized关键字来实现锁,在对队列进行操作时会对其进行加锁,保证同一时刻只有一个线程能够访问队列。 其次,阻塞队列内部会使用条件变量或信号量来实现线程间的协调与通信。当队列为空时,消费者线程需要等待直到队列有数据可取;当队列已满时,生产者线程需要等待直到队列有空位置可放入新数据。通过条件变量或信号量的等待和唤醒机制,实现了线程间的同步和互斥。 此外,阻塞队列通常还会使用一个循环数组来存储数据。循环数组在插入和删除元素时能够高效地利用已分配的内存空间,避免了频繁的扩容和内存拷贝。同时,循环数组的读写指针可以通过取模运算得到,实现环形循环。 最后,阻塞队列还会根据不同的需求提供不同的阻塞操作。例如,用于插入元素的put()方法队列已满时会阻塞直到有空位置可用,用于获取元素的take()方法队列为空时会阻塞直到有数据可取。这些阻塞操作的实现依赖于同步和协调机制,保证了线程安全和数据一致性。 总之,Java阻塞队列通过使用锁、条件变量或信号量、循环数组等机制,实现了线程安全和线程间的同步与通信。它是多线程编程常用的工具,能够有效地管理数据的生产和消费,提高多线程程序的可靠性和性能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值