09并发容器篇(D1_阻塞队列(D1_BlockingQueue))

一、知识回顾

了解队列时,还可以参考W哥以下篇章,以加深对队列的理解及系统学习!

  • JavaSE专栏:队列
  • 数据结构与算法专栏:队列

二、Queue(接口)

定义了队列的基本功能,添加、删除、查询。

满足FIFO(先进先出原则)。

public interface Queue<E> extends Collection<E> {
    //添加一个元素,添加成功返回true, 如果队列满了,就会抛出异常
    boolean add(E e);
    //添加一个元素,添加成功返回true, 如果队列满了,返回false
    boolean offer(E e);
    //返回并删除队首元素,队列为空则抛出异常
    E remove();
    //返回并删除队首元素,队列为空则返回null
    E poll();
    //返回队首元素,但不移除,队列为空则抛出异常
    E element();
    //获取队首元素,但不移除,队列为空则返回null
    E peek();
}

二、阻塞队列

1. 前言

JDK中提供了一系列场景的并发安全队列。

总的来说,按照实现方式的不同可分为阻塞队列和非阻塞队列。

阻塞队列使用锁实现,而非阻塞队列则使用 CAS 非阻塞算法实现。

2. 什么是阻塞队列

阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作支持阻塞的插入和移除方法。

  1. 支持阻塞的插入方法:意思是当队列满时,队列会阻塞插入元素的线程,直到队列不满。
  2. 支持阻塞的移除方法:意思是在队列为空时,获取元素的线程会等待队列变为非空。

阻塞队列常用于生产者和消费者的场景,生产者是向队列里添加元素的线程,消费者是 从队列里取元素的线程。

阻塞队列就是生产者用来存放元素、消费者用来获取元素的容器。

在阻塞队列不可用时,这两个附加操作提供了4种处理方式,如表6-1所示。

  • 抛出异常:当队列满时,如果再往队列里插入元素,会抛出IllegalStateException("Queue full")异常。当队列空时,从队列里获取元素会抛出NoSuchElementException异常。
  • 返回特殊值:当往队列插入元素时,会返回元素是否插入成功,成功返回true。如果是移 除方法,则是从队列里取出一个元素,如果没有则返回null。
  • 一直阻塞:当阻塞队列满时,如果生产者线程往队列里put元素,队列会一直阻塞生产者线程,直到队列可用或者响应中断退出。当队列空时,如果消费者线程从队列里take元素,队列会阻塞住消费者线程,直到队列不为空。
  • 超时退出:当阻塞队列满时,如果生产者线程往队列里插入元素,队列会阻塞生产者线程 一段时间,如果超过了指定的时间,生产者线程就会退出。

这两个附加操作的4种处理方式不方便记忆,所以我找了一下这几个方法的规律。

put和take分别尾首含有字母t,offer和poll都含有字母o。

注意:如果是无界阻塞队列,队列不可能会出现满的情况,所以使用put或offer方法永远不会被阻

塞,而且使用offer方法时,该方法永远返回true。

3. Java里面常见的阻塞队列

JDK 7 提供了 7 个阻塞队列,如下。

  • ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
  • LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。
  • PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
  • DelayQueue:一个使用优先级队列实现的无界阻塞队列。
  • SynchronousQueue:一个不存储元素的阻塞队列。
  • LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
  • LinkedBlockingDeque:一个由链表结构组成的双向无界阻塞队列。

注意 - 是针对AQS这一块

三、BlockingQueue(接口)

1. 前言

在上面,我们已经了解到什么事阻塞队列。

阻塞队列,顾名思义,首先他是一个队列常用的队列主要有以下两种方式

(当然通过不同的方式可以衍生出不同类别的队列方式,比如DelayQueue

  • 先进先出FIFO

先插入队列的元素也先出队列,类似排队的功能,从某种程度上来说,这种队列体现了公平性

  • 先进后出(LIFO)

后插入队列的元素,最先出队多线程环境,通过队列很容易实现数据共享,比如经典的生产者与消

费者模型中,通过队列可以很方便的实现数据共享。

假设我们有若干个生产者线程,又有若干个消费者线程。假设生产者线程把准备好的数据要共享给

消费者线程,利用队列来共享数据,就可以很方便的解决他们之间数据共享问题。但如果生产者跟

消费者在某个时间段内,万一发生处理数据速度不匹配的情况呢?

理想情况下,假如生产者产出数据的速度大于消费者消费数据的速度,并且生产数据累积到一定程

度的时候,那生产者必须暂停等待一下(阻塞生产者线程),以便消费者线程把累积的数据处理完

毕。反之亦然。然而在Concurent 包发布之前,在多线程环境下,我们每个程序员都要自己去控制

这些细节,尤其还要兼顾效率和安全。这些会给我们的程序带来不少的复杂度。好在这是强大的

Concurent 包横空出世了,而他也给我们带来了强大的 BlockQueue,(在多线程领域,所谓阻塞,

会挂起线程(即阻塞),一旦满足条件,被挂起的线程,又会自动被唤醒)

  • 当队列中没有数据的情况下,消费端的所有线程都会被阻塞,直到有数据放入队列
  • 当队列中装满了数据的情况下,生产者端的所有线程都会被阻塞(挂起),直到队列中有空余的位置,线程被自动唤醒

这也是我们在多线程环境下,为什么使用BlockQueue 的原因,作为BlockQueue的使用者,我们

再也不需要关心什么时候阻塞线程,什么时候唤醒线程,因为一起 BlockQueue 都给你包办了,既

然BlockQueue 如此申通广

大,让我们一起见识下他的方法。

2. 简介

在新增的 Concurent 包中,BlockQueue 很好解决了多线程中,如何高效传输数据的问题。

通过这些高效且线程安全的队列类,为我们搭建高质量的多线程程序带来极大的遍历。

在任意时刻只有一个线程可以进行take或者put操作,并且BlockingQueue提供了超时return null的

机制,在许BlockingQueue,是java.util.concurrent 包提供的用于解决并发生产者 - 消费者问题的

最有用的类,它的特性是多生产场景里都可以看到这个enq工具的身影。可以看到他是继承于JDK

的Queue。

接下来将会介绍BlockQueue家庭中的所有成员,包括他们的功能,一些经常使用的场景。

3. 特性

3.1. 队列类型

无限队列 (unbounded queue ) - 几乎可以无限增长

有限队列 ( bounded queue ) - 定义了最大容量

3.2. 队列数据结构

队列实质就是一种存储数据的结构,通常用链表或者数组实现。

一般而言队列具备FIFO先进先出的特性,当然也有双端队列(Deque)优先级队列

主要操作:入队(EnQueue)与出队(Dequeue)

4. 核心功能

入队(放入数据)

  • offer(anObject)

表示有可能的话,将Object 放入BlockQueue 中,如果BlockQueue 可以容纳,则返回true,否则返

回false,(本方法不阻塞当前线程)。

  • offer( E e,Long timeOut,TimeUnit unit)

可以设置指定的时间,如果指定时间内还没有往队列中加入,则返回false.

  • put(anObject)

把anObject 加入到队列中,如果BlockQueue 中没有空间,则调用此方法的线程被阻塞直到

BlockQueue有空间在继续。

出队(取出数据)

  • poll(time)

取走BlockQueue 中排在首位的对象,若不能及时取出,则等待time 参数规定的时间,取不出时

返回null.

  • poll(long time ,TimeUnit unit)

取出BlockQueue 中排在首位的对象,在指定时间内,一旦有数据就返回,超时还没有数据,则返

回失败

  • take()

取出BlockQueue 中排在首位的数据,如果BlockQueue 中没有数据,则当前线程一直阻塞,直到

有新的数据加入。

  • drainTo()

一次性的从BlockQueue中取出所有可用数据对象(还可以指定取出数据个数),通过该方法可以

提升获取数据效率,不需要多次加锁和释放锁,

  • peek()

获取队首元素,但不移除,队列为空则返回null

知识小结

当队列满了无法添加元素,或者是队列空了无法移除元素时:

抛出异常:add、remove、element

返回结果但不抛出异常:offer、poll、peek

阻塞:put、take

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodingW丨编程之路

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值