当前位置: 首页 > news >正文

多线程篇(阻塞队列- PriorityBlockingQueue)(持续更新迭代)

目录

一、简介

二、类图

三、源码解析

1. 字段讲解

2. 构造方法

3. 入队方法

put

浮调整比较器方法的实现

入队图解

4. 出队方法

take

dequeue

下沉调整比较器方法的实现

出队图解

四、总结


一、简介

PriorityBlockingQueue队列是 JDK1.5 的时候出来的一个阻塞队列。但是该队列入队的时候是不会阻塞的,

永远会加到队尾。

下面我们介绍下它的几个特点:

  • PriorityBlockingQueue 和 ArrayBlockingQueue 一样是基于数组实现的,但后者在初始化时需要指定长

度,前者默认长度是 11。

  • 该队列可以说是真正的无界队列,它在队列满的时候会进行扩容,而前面说的无界阻塞队列其实都有有界,只

是界限太大可以忽略(最大值是 2147483647)

  • 该队列属于权重队列,可以理解为它可以进行排序,但是排序不是从小到大排或从大到小排,是基于数组的堆

结构(具体如何排下面会进行分析)

  • 出队方式和前面的也不同,是根据权重来进行出队,和前面所说队列中那种先进先出或者先进后出方式不同。
  • 其存入的元素必须实现Comparator,或者在创建队列的时候自定义Comparator

注意:

  1. 堆结构实际上是一种完全二叉树,建议学习前了解一下二叉树。
  2. 堆又分为大顶堆和小顶堆。大顶堆中第一个元素肯定是所有元素中最大的,小顶堆中第一个元素是所有元素中

最小的。

二、类图

三、源码解析

1. 字段讲解

从下面的字段我们可以知道,该队列可以排序,使用显示锁来保证操作的原子性,

在空队列时,出队线程会堵塞等。

    /*** 默认数组长度*/private static final int DEFAULT_INITIAL_CAPACITY = 11;/*** 最大达容量,分配时超出可能会出现 OutOfMemoryError 异常*/private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;/*** 队列,存储我们的元素*/private transient Object[] queue;/*** 队列长度*/private transient int size;/*** 比较器,入队进行权重的比较*/private transient Comparator<? super E> comparator;/*** 显示锁*/private final ReentrantLock lock;/*** 空队列时进行线程阻塞的 Condition 对象*/private final Condition notEmpty;

2. 构造方法

    /*** 默认构造,使用长度为 11 的数组,比较器为空*/public PriorityBlockingQueue() {this(DEFAULT_INITIAL_CAPACITY, null);}/*** 自定义数据长度构造,比较器为空*/public PriorityBlockingQueue(int initialCapacity) {this(initialCapacity, null);}/*** 自定义数组长度,可以自定义比较器*/public PriorityBlockingQueue(int initialCapacity,Comparator<? super E> comparator) {if (initialCapacity < 1)throw new IllegalArgumentException();this.lock = new ReentrantLock();this.notEmpty = lock.newCondition();this.comparator = comparator;this.queue = new Object[initialCapacity];}

3. 入队方法

put

入队方法,下面可以看到 put 方法最终会调用 offer 方法,所以我们只看 offer 方法即可。

    public void put(E e) {offer(e); // never need to block}public boolean offer(E e) {//判断是否为空if (e == null)throw new NullPointerException();//显示锁final ReentrantLock lock = this.lock;lock.lock();//定义临时对象int n, cap;Object[] array;//判断数组是否满了while ((n = size) >= (cap = (array = queue).length))//数组扩容tryGrow(array, cap);try {//拿到比较器Comparator<? super E> cmp = comparator;//判断是否有自定义比较器if (cmp == null)//堆上浮siftUpComparable(n, e, array);else//使用自定义比较器进行堆上浮siftUpUsingComparator(n, e, array, cmp);//队列长度 +1size = n + 1;//唤醒休眠的出队线程notEmpty.signal();} finally {//释放锁lock.unlock();}return true;}

浮调整比较器方法的实现

    private static <T> void siftUpComparable(int k, T x, Object[] array) {Comparable<? super T> key = (Comparable<? super T>) x;while (k > 0) {//无符号向左移,目的是找到放入位置的父节点int parent = (k - 1) >>> 1;//拿到父节点的值Object e = array[parent];//比较是否大于该元素,不大于就没比较交换if (key.compareTo((T) e) >= 0)break;//以下都是元素位置交换array[k] = e;k = parent;}array[k] = key;}

根据上面的代码,可以看出这是完全二叉树在进行上浮调整。调整入队的元素,找出最小的,将元素排列有序化。

简单理解就是:父节点元素值一定要比它的子节点得小,如果父节点大于子节点了,那就两者位置进行交换。

入队图解

说的可能很模糊,我们先写个 demo,根据 demo 来进行图解分析:

public class TestPriorityBlockingQueue {public static void main(String[] args) throws InterruptedException {PriorityBlockingQueue<Integer> concurrentLinkedQueue = new PriorityBlockingQueue<Integer>();concurrentLinkedQueue.offer(10);concurrentLinkedQueue.offer(20);concurrentLinkedQueue.offer(5);concurrentLinkedQueue.offer(1);concurrentLinkedQueue.offer(25);concurrentLinkedQueue.offer(30);//输出元素排列concurrentLinkedQueue.stream().forEach(e-> System.out.print(e+"  "));//取出元素Integer take = concurrentLinkedQueue.take();System.out.println();concurrentLinkedQueue.stream().forEach(e-> System.out.print(e+"  "));}
}

上面可以看出,我们要入队的元素是 [10,20,5,1,21,30],接下来我们用图来演示一步步入队情况。

队列初始化时:

这时,我们开始将元素 元素 10 入队,并用二叉树辅助理解:

我们在将元素 20 入队:

将元素 5 入队后发现父节点大于子节点,这时需要进行上浮调整

开始进行上浮调整,将元素 10 和元素 5进行位置调换,结果如下:

接着将元素 1 入队后发现父节点大于子节点,继续进行调整:

第一次调整将元素 20 和元素 1 进行位置交换,交换完毕后结果如下:

交换完毕后,我们发现父节点的元素值还是大于子节点,说明还需要进行一次交换,最后交换结果如下:

接下来将元素 25 和 30 入队,结果如下:

注:

最小堆的的顶端一定是元素值最小的那个。

4. 出队方法

take

出队方法,该方法会阻塞

public E take() throws InterruptedException {//显示锁final ReentrantLock lock = this.lock;//可中断锁lock.lockInterruptibly();//结果接受对象E result;try {//判读队列是否为空while ( (result = dequeue()) == null)//线程阻塞notEmpty.await();} finally {lock.unlock();}return result;
}

dequeue

具体出队方法的实现

 private E dequeue() {//长度减少 1int n = size - 1;//判断队列中是否又元素if (n < 0)return null;else {//队列对象Object[] array = queue;//取出第一个元素E result = (E) array[0];//拿出最后一个元素E x = (E) array[n];//置空array[n] = null;Comparator<? super E> cmp = comparator;if (cmp == null)//下沉调整siftDownComparable(0, x, array, n);elsesiftDownUsingComparator(0, x, array, n, cmp);//成功则减少队列中的元素数量size = n;return result;}
}

总体就是找到父节点与两个子节点中最小的一个节点,然后进行交换位置,不断重复,由上而下的交换。

下沉调整比较器方法的实现

private static <T> void siftDownComparable(int k, T x, Object[] array,int n) {//判断队列长度if (n > 0) {Comparable<? super T> key = (Comparable<? super T>)x;//找到队列最后一个元素的父节点的索引。//如下图最大元素是30 父节点是 10,对于索引是 2int half = n >>> 1;           // loop while a non-leafwhile (k < half) {//拿到 k 节点下的左子节点int child = (k << 1) + 1; // assume left child is least//取得子节点对应的值Object c = array[child];//取得 k 右子节点的索引int right = child + 1;//比较右节点的索引是否小于队列长度和左右子节点的值进行比较if (right < n &&((Comparable<? super T>) c).compareTo((T) array[right]) > 0)c = array[child = right];//比较父节点值是否大于子节点if (key.compareTo((T) c) <= 0)break;//下面都是元素替换array[k] = c;k = child;}array[k] = key;}
}

出队图解

这时,我们需要从队列中取出第一个元素 1,元素 1 取出时会与队列中最后一个元素进行交换,并将最后一个元素

置空。(实际上源码不是这么做的,源代码中是用变量来保存索引,直到全部下沉调整完成才进行替换)

替换后,结果就如下图显示一样。我们发现父节点大于子节点了,所以还需要再一次进行替换操作。

再一次替换后,将元素 30 下沉到下一个左边子节点,子节点上浮到原父节点位置。这就完成了下沉调整了。

四、总结

PriorityBlockingQueue 真的是个神奇的队列,可以实现优先出队。

最特别的是它只有一个锁,入队操作永远成功,而出队只有在空队列

的时候才会进行线程阻塞。可以说有一定的应用场景吧,比如:有任务要执行,可以对任务加一个优先级的权重,

这样队列会识别出来,对该任务优先进行出队。

相关文章:

多线程篇(阻塞队列- PriorityBlockingQueue)(持续更新迭代)

目录 一、简介 二、类图 三、源码解析 1. 字段讲解 2. 构造方法 3. 入队方法 put 浮调整比较器方法的实现 入队图解 4. 出队方法 take dequeue 下沉调整比较器方法的实现 出队图解 四、总结 一、简介 PriorityBlockingQueue队列是 JDK1.5 的时候出来的一个阻塞…...

strstr函数的使用和模拟实现

目录 1.头文件 2.strstr函数的使用 3.strstr函数模拟实现 小心&#xff01;VS2022不可直接接触&#xff0c;否则&#xff01;没这个必要&#xff0c;方源面色淡然一把抓住&#xff01;顷刻炼化&#xff01; 1.头文件 strstr函数的使用需要头文件 #include<string.h>…...

使用Selenium与WebDriver实现跨浏览器自动化数据抓取

背景/引言 在数据驱动的时代&#xff0c;网络爬虫成为了收集和分析海量数据的关键工具。为了应对不同浏览器环境下的兼容性问题&#xff0c;Selenium与WebDriver成为了开发者实现跨浏览器自动化数据抓取的首选工具。本文将深入探讨如何利用Selenium和WebDriver实现跨浏览器的数…...

信创实践(3):基于x2openEuler将CentOS升级成openEuler,享受其带来的创新和安全特性

引言&#xff1a; 在当前的 IT 行业中&#xff0c;创新和安全性是两大关键趋势。随着 CentOS 停止维护&#xff0c;许多用户正在寻找替代方案&#xff0c;以保持其系统的更新和安全。openEuler 作为一个强大的开源操作系统&#xff0c;成为了理想的迁移目标。本教程将指导您如…...

LEAN 类型理论之注解(Annotations of LEAN Type Theory)-- 相等类型(Equality Type)

《何谓相等 (Equality)&#xff0c;在类型理论(Type Theory)语境下》 与 《转化&#xff08;conversion and reduction&#xff09;后的相等&#xff08;Equality&#xff09;》&#xff0c;两文中&#xff0c;已对相等&#xff08;Equality&#xff09;的概念进行了描述&#…...

Idea 创建 Maven项目的时候卡死

文章目录 一、Archetype 和 Catalog1.1 Archetype&#xff08;原型&#xff09;1.2 Catalog&#xff08;目录&#xff09; 二、可能遇到的问题2.1 问题描述2.2 原因分析2.3 解决方案 参考资料 一、Archetype 和 Catalog 1.1 Archetype&#xff08;原型&#xff09; Archetype…...

C++入门(02)简单了解C++应用程序的开发部署

文章目录 1. 开发C应用程序2. 简单示例计算器程序3. 需求分析4. 设计5. 编码6. 编译7. 调试8. 测试9. 部署10. 部署示例10.1 使用Visual Studio Installer Projects创建安装程序10.2 安装VisualStudio Installer Projects扩展10.3 在calculator解决方案中创建安装项目10.3.1 添…...

有了室内外一体化人行导航,你还怕迷路吗?

在快节奏的现代生活中&#xff0c;无论是穿梭于繁华的都市丛林&#xff0c;还是漫步于错综复杂的购物中心&#xff0c;迷路似乎成了不少人的“小确丧”。然而&#xff0c;随着科技的飞速发展&#xff0c;一项革命性的创新——室内外一体化人行导航系统&#xff0c;正悄然改变着…...

Python虚拟环境包迁移

1. 激活源虚拟环境 首先&#xff0c;激活你想要导出包的源虚拟环境。在命令行中输入&#xff1a; Windows: path\to\your\source_env\Scripts\activatemacOS/Linux: source path/to/your/source_env/bin/activate 2. 导出已安装包的列表 使用以下命令生成一个requirements…...

利用分布式锁在ASP.NET Core中实现防抖

前言 在 Web 应用开发过程中&#xff0c;防抖&#xff08;Debounce&#xff09; 是确保同一操作在短时间内不会被重复触发的一种有效手段。常见的场景包括防止用户在短时间内重复提交表单&#xff0c;或者避免多次点击按钮导致后台服务执行多次相同的操作。无论在单机环境中&a…...

Django+Vue3前后端分离学习(二)(重写User类)

一、重写User类&#xff1a; 1、首先导入User类&#xff1a; from django.contrib.auth.models import User 2、然后点在User上&#xff0c;按住ctrl 点进去&#xff0c;发现 User类继承AbstractUser Ctrl点进去AbstractUser&#xff0c;然后将此方法全部复制到自己APP的mo…...

兔英语语法体系——观后笔记

目录 一、视频链接 二、视频前言 三、简单句(Simple Sentences) 1. 可独立完成的动作 2. 有1个动作的承受者 3. 有两个动作承受者 4. 只有一个动作承受者(但需补充) 5. 非 “动作” 6. 总结 四、五大基本句型 五、句子成分 6. 定语 7. 状语 8. 同位语 9. 总结 …...

哈希表如何避免冲突

系列文章&#xff1a; 1. 先导片--Map&Set之二叉搜索树 2. Map&Set之相关概念 3. 哈希表如何避免冲突 目录 1.概念 2. 冲突-概念 3. 冲突-避免 3.1 冲突-避免-哈希函数设计 3.2 冲突-避免-负载因子调节 4. 冲突-解决 4.1 冲突-解决-闭散列 4.1.1 线性探…...

内核模块驱动开发

内核模块开始学习前&#xff0c;一定是最先接触到内核模块三要素(面试)&#xff0c;驱动入口、驱动出口和协议的遵循。 1.内核模块三要素(面试)//修饰模块化驱动的入口函数module_init(demo_init);//修饰模块化驱动的出口函数module_eixt(demo_exit);//遵循GPL开源协议MODULE_…...

Linux 下 alsa 库录音并保存为 WAV 格式

麦克风列表&#xff1a; [jnjn build]$ arecord -l **** List of CAPTURE Hardware Devices **** card 0: AudioPCI [Ensoniq AudioPCI], device 0: ES1371/1 [ES1371 DAC2/ADC]Subdevices: 1/1Subdevice #0: subdevice #0 card 1: Camera [2K USB Camera], device 0: USB Aud…...

使用stripe进行在线支付、退款、订阅、取消订阅功能(uniapp+h5)

stripe官网:Stripe 登录 | 登录 Stripe 管理平台 然后在首页当中打开测试模式,使用测试的公钥跟私钥进行开发 测试卡号 4242 4242 4242 4242 1234 567 在线支付 stripe的在线支付有两种,第一种就是无代码,第二中就是使用api进行自定义,一般来说推荐第二种进行开发 无…...

深度学习中常见的损失函数

关注B站可以观看更多实战教学视频&#xff1a;hallo128的个人空间 深度学习中常见的损失函数 损失函数的作用 损失函数是衡量神经网络输出与真实标签之间差距的指标。在训练过程中&#xff0c;神经网络的目标是最小化损失函数的值。常见的损失函数包括均方误差&#xff08;MS…...

认识Linux及Linux的环境搭建

目录 1、什么是Linux2、Linux环境搭建2.1 下载安装 Xshell2.2 下载安装 VMware Workstation Pro2.3 选择适合自己系统 1、什么是Linux Linux&#xff0c;一般指GNU/Linux&#xff08;单独的Linux内核并不可直接使用&#xff0c;一般搭配GNU套件&#xff0c;故得此称呼&#xff…...

Java之线程篇三

​​​​​​​ 目录 线程状态 观察线程的所有状态 线程状态及其描述 线程状态转换 代码示例1 代码示例2 线程安全 概念 线程不安全的代码示例 线程不安全的原因 线程安全的代码示例-加锁 synchronized关键字 synchronized的特性 小结 形成死锁的四个必要条件 …...

Bootstrap动态设置表格title项

页面searchType <form id"formId"><div class"select-list"><ul><li><select name"searchType" id"searchType"><option value"1">按各节点统计</option><option value"…...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分&#xff1a; 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析&#xff1a; CTR…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件

在选煤厂、化工厂、钢铁厂等过程生产型企业&#xff0c;其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进&#xff0c;需提前预防假检、错检、漏检&#xff0c;推动智慧生产运维系统数据的流动和现场赋能应用。同时&#xff0c;…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢

随着互联网技术的飞速发展&#xff0c;消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁&#xff0c;不仅优化了客户体验&#xff0c;还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用&#xff0c;并…...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解&#xff08;7大核心步骤实战技巧&#xff09; 一、爬虫完整工作流程 以下是爬虫开发的完整流程&#xff0c;我将结合具体技术点和实战经验展开说明&#xff1a; 1. 目标分析与前期准备 网站技术分析&#xff1a; 使用浏览器开发者工具&#xff08;F12&…...

大模型多显卡多服务器并行计算方法与实践指南

一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)

目录 一、&#x1f44b;&#x1f3fb;前言 二、&#x1f608;sinx波动的基本原理 三、&#x1f608;波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、&#x1f30a;波动优化…...

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…...

Docker 本地安装 mysql 数据库

Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker &#xff1b;并安装。 基础操作不再赘述。 打开 macOS 终端&#xff0c;开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...