前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >阻塞队列 BlockingQueue 我也不会啊

阻塞队列 BlockingQueue 我也不会啊

作者头像
韩旭051
发布于 2021-04-14 07:16:35
发布于 2021-04-14 07:16:35
76600
代码可运行
举报
文章被收录于专栏:刷题笔记刷题笔记
运行总次数:0
代码可运行

阻塞队列BlockingQueue

BlockingQueue中有哪些方法,为什么这样设计?

参考答案

为了应对不同的业务场景,BlockingQueue 提供了4 组不同的方法用于插入、移除以及对队列中的元素进行检查。如果请求的操作不能得到立即执行的话,每组方法的表现是不同的。这些方法如下:

-

抛异常

特定值

阻塞

超时

插入

add(e)

offer(e)

put(e)

offer(e, time, unit)

移除

remove()

poll()

take()

poll(time, unit)

检查

element()

peek()

四组不同的行为方式含义如下:

  • 抛异常:如果操作无法立即执行,则抛一个异常;
  • 特定值:如果操作无法立即执行,则返回一个特定的值(一般是 true / false)。
  • 阻塞:如果操作无法立即执行,则该方法调用将会发生阻塞,直到能够执行;
  • 超时:如果操作无法立即执行,则该方法调用将会发生阻塞,直到能够执行。但等待时间不会超过给定值,并返回一个特定值以告知该操作是否成功(典型的是true / false)。

BlockingQueue是怎么实现的?

参考答案

BlockingQueue是一个接口,它的实现类有ArrayBlockingQueue、DelayQueue、 LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue等。它们的区别主要体现在存储结构上或对元素操作上的不同,但是对于put与take操作的原理是类似的。下面以ArrayBlockingQueue为例,来说明BlockingQueue的实现原理。

首先看一下ArrayBlockingQueue的构造函数,它初始化了put和take函数中用到的关键成员变量,这两个变量的类型分别是ReentrantLock和Condition。ReentrantLock是AbstractQueuedSynchronizer(AQS)的子类,它的newCondition函数返回的Condition实例,是定义在AQS类内部的ConditionObject类,该类可以直接调用AQS相关的函数。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public ArrayBlockingQueue(int capacity, boolean fair) {
    if (capacity <= 0)
        throw new IllegalArgumentException();
    this.items = new Object[capacity];
    lock = new ReentrantLock(fair);
    notEmpty = lock.newCondition();
    notFull =  lock.newCondition();
}

put函数会在队列末尾添加元素,如果队列已经满了,无法添加元素的话,就一直阻塞等待到可以加入为止。函数的源码如下所示。我们会发现put函数使用了wait/notify的机制。与一般生产者-消费者的实现方式不同,同步队列使用ReentrantLock和Condition相结合的机制,即先获得锁,再等待,而不是synchronized和wait的机制。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void put(E e) throws InterruptedException {
    checkNotNull(e);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == items.length)
            notFull.await();
        enqueue(e);
    } finally {
        lock.unlock();
    }
}

再来看一下消费者调用的take函数,take函数在队列为空时会被阻塞,一直到阻塞队列加入了新的元素。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == 0)
            notEmpty.await();
        return dequeue();
    } finally {
        lock.unlock();
    }
}

扩展阅读

await操作:

  • 我们发现ArrayBlockingQueue并没有使用Object.wait,而是使用的Condition.await,这是为什么呢?Condition对象可以提供和Object的wait和notify一样的行为,但是后者必须先获取synchronized这个内置的monitor锁才能调用,而Condition则必须先获取ReentrantLock。这两种方式在阻塞等待时都会将相应的锁释放掉,但是Condition的等待可以中断,这是二者唯一的区别。
  • 我们先来看一下Condition的await函数,await函数的流程大致如下图所示。await函数主要有三个步骤,一是调用addConditionWaiter函数,在condition wait queue队列中添加一个节点,代表当前线程在等待一个消息。然后调用fullyRelease函数,将持有的锁释放掉,调用的是AQS的函数。最后一直调用isOnSyncQueue函数判断节点是否被转移到sync queue队列上,也就是AQS中等待获取锁的队列。如果没有,则进入阻塞状态,如果已经在队列上,则调用acquireQueued函数重新获取锁。

signal操作:

  • signal函数将condition wait queue队列中队首的线程节点转移等待获取锁的sync queue队列中。这样的话,await函数中调用isOnSyncQueue函数就会返回true,导致await函数进入最后一步重新获取锁的状态。
  • 我们这里来详细解析一下condition wait queue和sync queue两个队列的设计原理。condition wait queue是等待消息的队列,因为阻塞队列为空而进入阻塞状态的take函数操作就是在等待阻塞队列不为空的消息。而sync queue队列则是等待获取锁的队列,take函数获得了消息,就可以运行了,但是它还必须等待获取锁之后才能真正进行运行状态。
  • signal函数其实就做了一件事情,就是不断尝试调用transferForSignal函数,将condition wait queue队首的一个节点转移到sync queue队列中,直到转移成功。因为一次转移成功,就代表这个消息被成功通知到了等待消息的节点。

signal函数的示意图如下所示。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/03/19 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
BlockingQueue与Condition原理解析
 我在前段时间写了一篇关于AQS源码解析的文章AbstractQueuedSynchronizer超详细原理解析AbstractQueuedSynchronizer超详细原理解析
程序员历小冰
2019/01/16
7570
BlockingQueue与Condition原理解析
阻塞队列BlockingQueue及实现原理分析
BlockingQueue 对插入操作、移除操作、获取元素操作提供了四种不同的方法用于不同的场景中使用:
烂猪皮
2023/09/04
5120
阻塞队列BlockingQueue及实现原理分析
并发阻塞队列BlockingQueue解读
首先,最基本的来说, BlockingQueue 是一个先进先出的队列(Queue),为什么说是阻塞(Blocking)的呢?是因为 BlockingQueue 支持当获取队列元素但是队列为空时,会阻塞等待队列中有元素再返回;也支持添加元素时,如果队列已满,那么等到队列可以放入新元素时再放入。
大忽悠爱学习
2022/10/24
1.1K0
并发阻塞队列BlockingQueue解读
(juc系列)阻塞队列(blockingqueue)及其实现
阻塞队列的方法,有四种形式来处理,操作没有办法被立刻满足,但是未来某些时间点可能满足的情况:
呼延十
2021/11/10
4810
Java 7 种阻塞队列详解
队列(Queue)是一种经常使用的集合。Queue 实际上是实现了一个先进先出(FIFO:First In First Out)的有序表。和 List、Set 一样都继承自 Collection。它和 List 的区别在于,List可以在任意位置添加和删除元素,而Queue 只有两个操作:
海星
2020/09/27
9.7K0
【原创】Java并发编程系列31 | 阻塞队列(上)
阻塞队列在并发编程非常常用,被广泛使用在“生产者-消费者”问题中。接下来两篇文章就来详细介绍阻塞队列。本文是阻塞队列上篇。
java进阶架构师
2020/08/17
4520
【原创】Java并发编程系列31 | 阻塞队列(上)
基于数组的有界阻塞队列 —— ArrayBlockingQueue
" 在阅读完和 AQS 相关的锁以及同步辅助器之后,来一起阅读 JUC 下的和队列相关的源码。先从第一个开始:ArrayBlockingQueue。 "
程序员小航
2020/11/23
9370
基于数组的有界阻塞队列 —— ArrayBlockingQueue
多线程之阻塞队列
DelayQueue每次都是将元素加入排序队列,以delay/过期时间为排序因素,将快过期的元素放在队首,取数据的时候每次都是先取快过期的元素。 构造方法
OPice
2019/10/23
9510
深入理解阻塞队列
阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作支持阻塞的插入和移除方法。
Java识堂
2019/08/13
2490
阻塞队列实现之ArrayBlockingQueue源码解析
ArrayBlockingQueue是由数组构成的有界阻塞队列,支持FIFO的次序对元素进行排序。
烂猪皮
2023/09/04
1620
阻塞队列实现之ArrayBlockingQueue源码解析
并发编程4:Java 阻塞队列源码分析(上)
上篇文章 并发编程3:线程池的使用与执行流程 中我们了解到,线程池中需要使用阻塞队列来保存待执行的任务。这篇文章我们来详细了解下 Java 中的阻塞队列究竟是什么。 什么是阻塞队列 阻塞队列其实就是生
张拭心 shixinzhang
2018/01/05
1.5K0
并发编程4:Java 阻塞队列源码分析(上)
一文带你彻底掌握阻塞队列!
在之前的文章中,我们介绍了生产者和消费者模型的最基本实现思路,相信大家对它已经有一个初步的认识。
Java极客技术
2023/11/16
8060
一文带你彻底掌握阻塞队列!
Java并发编程--BlockingQueue
  BlockingQueue支持两个附加操作的Queue:1)当Queue为空时,获取元素线程被阻塞直到Queue变为非空;2)当Queue满时,添加元素线程被阻塞直到Queue不满。BlockingQueue不允许元素为null,如果入队一个null元素,会抛NullPointerException。常用于生产者消费者模式。
在周末
2019/09/11
5680
BlockingQueue 源码分析
BlockingQueue 首先是一个队列,其次提供了阻塞功能。它看起来很像消息队列可让消息解耦,但其在生产者-消费者模型中通过阻塞又可使二者速度达到平衡。使用阻塞队列无需过多考虑线程安全问题,专注业务逻辑的实现即可
晚上没宵夜
2022/05/09
2610
BlockingQueue 源码分析
多线程应用 - 阻塞队列ArrayBlockingQueue详解
ArrayBlockingQueue是一个阻塞式的先进先出队列。该结构具有以下三个特点:
虞大大
2020/08/26
1.5K0
聊聊 JDK 阻塞队列源码(ReentrantLock实现)
项目中用到了一个叫做 Disruptor 的队列,今天楼主并不是要介绍 Disruptor 而是想巩固一下基础扒一下 JDK 中的阻塞队列,听到队列相信大家对其并不陌生,在我们现实生活中队列随处可见,最经典的就是去银行办理业务等。 当然在计算机世界中,队列是属于一种数据结构,队列采用的FIFO(first in firstout),新元素(等待进入队列的元素)总是被插入到尾部,而读取的时候总是从头部开始读取。在计算中队列一般用来做排队(如线程池的等待排队,锁的等待排队),用来做解耦(生产者消费者模式),异步等等。
haifeiWu
2018/09/11
3510
(72) 显式条件 / 计算机程序的思维逻辑
上节我们介绍了显式锁,本节介绍关联的显式条件,介绍其用法和原理。显式条件也可以被称做条件变量、条件队列、或条件,后文我们可能会交替使用。 用法 基本概念和方法 锁用于解决竞态条件问题,条件是线程间的协作机制。显式锁与synchronzied相对应,而显式条件与wait/notify相对应。wait/notify与synchronized配合使用,显式条件与显式锁配合使用。 条件与锁相关联,创建条件变量需要通过显式锁,Lock接口定义了创建方法: Condition newCondition(); C
swiftma
2018/01/31
7910
BlockingQueue
BlockingQueue 是一个先进先出的队列(Queue), 并且当获取队列元素但是队列为空时,会阻塞等待队列中有元素再返回;也支持添加元素时,如果队列已满,那么等到队列可以放入新元素时再放入。
leobhao
2022/06/28
3040
Java并发-BlockingQueue
阻塞队列(BlockingQueue)是一个支持两种附加操作的队列。支持附加阻塞的插入和移除操作。
lpe234
2021/03/02
5710
Java面试——阻塞队列
【1】阻塞队列的架构图:阻塞队列与 List 具有很多类似之处,对比着学习会更加容易一些。
Java架构师必看
2021/05/14
9630
Java面试——阻塞队列
相关推荐
BlockingQueue与Condition原理解析
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验