Java阻塞队列必看类:BlockingQueue快速了解大体框架和实现思路

2022-11-13 19:11:20 队列 阻塞 必看

前言

本文主要说明BlockingQueue类、阻塞队列使用的共同父类AbstractQueue的基础知识。后续将会逐渐探索BlockingQueue的所有实现类。

BlockingQueue

概览

1. 不接受null元素。

通过新增方法添加null元素时,会抛出空指针异常。因为null值将用作标记​​值,来指明poll操作失败。同时null值作为阻塞队列的元素也是无任何意义的。

2. 可以有容量限制,也可以没有。

当作为有界队列时,如果当队列空间不足时,add操作将会抛出IllegalStateException异常,offer将会返回false,put操作将会阻塞直到队列空间可用。

如果不设置容量大小,那么默认值是Integer.MAX_VALUE。此时可以看作无界队列;

虽然队列在逻辑上可以是无界的,但由于资源耗尽(导致OutOfMemoryError ),添加操作可能会失败。 

3. 用于生产者-消费者队列,但是同时也支持Collection的操作。

例如删除队列元素操作remove(e),但是这些操作的效率很低,一般仅用于删除某个临时取消的任务。

4. 所有实现都是线程安全的。

但是对于集合中的批量操作方法,如果其实现类没有特殊处理的话,那么addAll、containsAll、retainAll、removeAll等可能不会原子地执行。

5. 队列,整体上都基本准寻先进先出的访问顺序。

在整体上看,各个实现类是符合FIFO的顺序的。

主要实现类

  • LinkedBlockingQueue链表式阻塞队列
  • ArrayBlockingQueue数组式阻塞队列
  • PriorityBlockingQueue:优先级阻塞队列
  • LinkedBlockingDeque:阻塞型双端队列
  • DelayQueue:延时阻塞队列
  • SynchronousQueue:实时同步队列(没有任何存储元素的空间)
  • …………

BlockingQueue方法的四类形式

BlockingQueue的所有实现类的方法都分为如下四类。相对于其他的集合操作方法,put和take是实现阻塞操作的核心方法,需要重点关注。

  • Throws exception:操作未实现时(正常流程下的执行)抛出异常
  • Special value:根据操作的实际情况,返回特定值,例如null、false
  • Blocks:阻塞当前线程,直到当前线程可以成功执行
  • Times out:尝试指定时间后,放弃执行
 

Throws exception

Special value

Blocks

Times out

新增

add(E e)

offer(E e)

put(E e)

offer(E e, long timeout, TimeUnit unit)

删除

remove()

poll()

take()

poll(long timeout, TimeUnit unit)

查询

element()

peek()

  

BlockingQueue类确定了阻塞队列的整体框架,确定了各个方法的定义,因此也是了解阻塞队列一定要学习的类。

本文中大部分都是从源码注释中解读而来,同时也包含了一些自己的理解,如果表述有误,还望指出。

AbstractQueue

概览

AbstractQueue类,提供了一些Queue操作的骨架实现。

这里提及AbstractQueue的原因是,阻塞队列的大多实现类都继承该类,可以说,这个抽象类,实现了BlockingQueue的大部分通用功能。比如add、remove失败时抛出异常等等,下面会一一提及。

源码解析

这个类基本上搭建起了一个阻塞队列Throws exception形式的框架,它实现了各个抛出异常的方法。

各个方法都比较简单,关键在于了解其具体的实现思路。这对了解各个阻塞队列的实现类很有帮助。

  • add
    public boolean add(E e) {
        if (offer(e))
            return true;
        else
            throw new IllegalStateException("Queue full");
    }

当队列空间足够时,将直接插入成功。offer方法由具体的子类实现。

如果offer插入元素成功,则add返回true ,否则将抛出IllegalStateException异常。

  • remove
    public E remove() {
        E x = poll();
        if (x != null)
            return x;
        else
            throw new NoSuchElementException();
    }

检索并删除队列的队首元素。

它与poll不同之处仅在于,如果此队列为空,它会抛出NoSuchElementException异常。

删除成功后,返回删除的元素。

  • element
    public E element() {
        E x = peek();
        if (x != null)
            return x;
        else
            throw new NoSuchElementException();
    }

仅获取队列的队首元素并返回结果,并不进行操作。

与peek不同之处在于,如果此队列为空,它会引发NoSuchElementException异常。

  • clearaddAll
    public void clear() {
        while (poll() != null)
            ;
    } 
 
    public boolean addAll(Collection<? extends E> c) {
        if (c == null)
            throw new NullPointerException();
        if (c == this)
            throw new IllegalArgumentException();
        boolean modified = false;
        for (E e : c)
            if (add(e))
                modified = true;
        return modified;
    }

遍历添加或删除所有元素,实现挺简单的,不过这里并没有进行单个操作出现异常时的处理。

总结

阻塞队列了解得也差不多了,之前只是简单的学习了如何使用阻塞队列的方法,这次也是打算来一个全面深入的学习吧。知其然知其所以然,对于程序员还是蛮重要的。 

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

相关文章