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

多线程与高并发——并发编程(4)

文章目录

  • 四、阻塞队列
    • 1 基础概念
      • 1.1 生产者消费者概念
      • 1.2 JUC阻塞队列的存取方法
    • 2 ArrayBlockingQueue
      • 2.1 ArrayBlockingQueue的基本使用
      • 2.2 生产者方法实现原理
        • 2.2.1 ArrayBlockingQueue的常见属性
        • 2.2.2 add方法
        • 2.2.3 offer方法
        • 2.2.4 offer(time,unit)方法
        • 2.2.5 put方法
      • 2.3 消费者方法实现原理
        • 2.3.1 remove方法
        • 2.3.2 poll方法
        • 2.3.3 poll(timeout,unit)方法
        • 2.3.4 take方法
        • 2.3.5 虚假唤醒
    • 3 LinkedBlockingQueue
      • 3.1 LinkedBlockingQueue的底层实现
      • 3.2 生产者方法实现原理
        • 3.2.1 add方法
        • 3.2.2 offer方法
        • 3.2.3 offer(time,unit)方法
        • 3.2.4 put方法
      • 3.3 消费者方法实现原理
        • 3.3.1 remove方法
        • 3.3.2 poll方法
        • 3.3.3 poll(time,unit)方法
        • 3.3.4 take方法
    • 4 PriorityBlockingQueue
      • 4.1 PriorityBlockingQueue介绍
      • 4.2 二叉堆结构介绍
      • 4.3 PriorityBlockingQueue核心属性
      • 4.4 PriorityBlockingQueue的写入操作
        • 4.4.1 offer方法基本流程
        • 4.4.2 offer扩容操作
        • 4.4.3 offer添加数据
      • 4.5 PriorityBlockingQueue的读取操作
        • 4.5.1 查看获取方法
        • 4.5.2 查看dequeue获取数据
        • 4.5.3 下移做平衡操作
    • 5 DelayQueue
      • 5.1 DelayQueue介绍&应用
      • 5.2 DelayQueue核心属性
      • 5.3 DelayQueue写入流程分析
      • 5.4 DelayQueue读取流程分析
    • 6 SynchronousQueue
      • 6.1 SynchronousQueue介绍
      • 6.2 SynchronousQueue核心属性
      • 6.3 SynchronousQueue的TransferQueue源码
      • 6.4 tansfer方法流程图

四、阻塞队列

1 基础概念

1.1 生产者消费者概念

生产者-消费者是设计模式的一种,让生产者和消费者基于一个容器来解决强耦合的问题。生产者与消费者彼此之间不会直接通讯,而是通过一个容器(队列)进行通讯。

  • 生产者生产完数据后扔到容器中,不用等消费者来处理;
  • 消费者也不需要去找生产者要数据,直接从容器中获取即可;
  • 而这种容器最常用的结构就是队列。

1.2 JUC阻塞队列的存取方法

常用的存取方法都来自 JUC 包下的 BlockingQueue

  • 生产者存储方法:
    • add(E):添加数据到队列,若队列满了,抛出异常;
    • offer(E):添加数据到队列,若队列满了,返回 false;
    • offer(E,timeout,unit):添加数据到队列,若队列满了,阻塞 timeout 时间,超时后返回 false;
    • put(E):添加数据到队列,若队列满了,挂起线程,等到队列中有位置,再扔数据进去,死等。
  • 消费者取数据方法:
    • remove():从队列中移除数据,若队列为空,抛出异常;
    • poll():从队列中移除数据,若队列为空,返回 false;
    • poll(timeout,unit):从队列中移除数据,若队列为空,阻塞 timeout 时间,等生产者仍数据再获取数据,超时后返回 false;
    • take():从队列中移除数据,若队列为空,挂起线程,一直等生产者仍数据再获取。

2 ArrayBlockingQueue

2.1 ArrayBlockingQueue的基本使用

  • ArrayBlockingQueue 在初始化时,必须指定当前队列的长度,因为 ArrayBlockingQueue 是基于数组实现的队列结构,数组长度不可变,必须提前设置数据长度信息。
public static void main(String[] args) throws InterruptedException {// 必须设置队列长度ArrayBlockingQueue queue = new ArrayBlockingQueue(4);// 生产者生产数据queue.add("1");queue.offer("2");queue.offer("3", 2, TimeUnit.SECONDS);queue.put("4");// 消费者消费数据System.out.println(queue.remove());System.out.println(queue.poll());System.out.println(queue.poll(2, TimeUnit.SECONDS));System.out.println(queue.take());
}

2.2 生产者方法实现原理

  • 生产者添加数据到队列的方法比较多,需要一个一个看

2.2.1 ArrayBlockingQueue的常见属性

ArrayBlockingQueue中的成员变量

final Object[] items; 				// 就是数组本身
int takeIndex;						// 取数据的下标
int putIndex;						// 存数据的下标
int count;							// 当前数组中元素的个数
final ReentrantLock lock;			// 就是一个 ReentrantLock 锁
private final Condition notEmpty;	// 消费者挂起线程和唤醒线程用到的Condition(可看作是synchronized的wait和notify)
private final Condition notFull;	// 生产者挂起线程和唤醒线程用到的Condition(可看作是synchronized的wait和notify)

2.2.2 add方法

  • add方法本身就是调用了offer方法,如果offer方法返回false,直接抛出异常
public boolean add(E e) {if (offer(e))return true;else 	// 抛出的异常throw new IllegalStateException("Queue full");
}

2.2.3 offer方法

public boolean offer(E e) {checkNotNull(e);	// 要求存储的数据不允许为null,否则抛出空指针异常// 拿到当前阻塞队列的lock锁final ReentrantLock lock = this.lock;lock.lock();	// 为保证线程安全,加锁try {// 判断队列中元素是否满了,若满了,则返回falseif (count == items.length)return false;else {// 队列没满,执行 enqueue 将元素添加到队列中,并返回trueenqueue(e);return true;}} finally {lock.unlock();		// 操作完释放锁}
}
// ================
private void enqueue(E x) {// 拿到数组的引用,将元素放到指定的位置final Object[] items = this.items;items[putIndex] = x;// 对putIndex进行++操作,并判断是否等于数组长度,需要归为if (++putIndex == items.length)putIndex = 0;	// 归位:将索引值设置为0count++;	// 添加成功,数据++notEmpty.signal();	// 将一个Condition中阻塞的线程唤醒
}

2.2.4 offer(time,unit)方法

生产者在添加数据时,如果队列已经满,阻塞一会:

  • 阻塞到消费者消费了消息,然后唤醒当前阻塞线程;
  • 阻塞到了 timeout 时间,再次判断是否可以添加,若不能直接告辞。
// 线程在挂起时,如果对当前阻塞线程的终端标记位进行设置,会抛出异常直接结束
public boolean offer(E e, long timeout, TimeUnit unit)throws InterruptedException {// 非空校验checkNotNull(e);long nanos = unit.toNanos(timeout);		// 将时间单位转为纳秒final ReentrantLock lock = this.lock;	// 加锁lock.lockInterruptibly();	// 允许线程中断排除异常的加锁方法try {// 为什么是while(虚假唤醒)while (count == items.length) {	// 如果元素个数和数组长度一致,说明队列满了if (nanos <= 0)	// 判断等待时间是否充裕return false;	// 不充裕,直接添加失败,返回false// 挂起等待,会同时释放锁资源(对标 synchronized 的wait方法)// awaitNanos会挂起线程,并且返回剩余的阻塞时间,恢复执行时,需要重新获取锁资源nanos = notFull.awaitNanos(nanos);}enqueue(e); // 这里锁门队列有空间了,enqueue将数据添加到阻塞队列中,并返回truereturn true;} finally {lock.unlock();	// 是否锁资源}
}

2.2.5 put方法

  • 如果队列是满的,就一直挂起,直到被唤醒,或者被中断
public void put(E e) throws InterruptedException {checkNotNull(e);final ReentrantLock lock = this.lock;lock.lockInterruptibly();try {while (count == items.length)// await方法会一直阻塞,直到被唤醒或者被中断notFull.await();enqueue(e);} finally {lock.unlock();}
}

2.3 消费者方法实现原理

2.3.1 remove方法

  • remove方法本身就是调用了poll方法,如果poll方法返回null,直接抛出异常
public E remove() {E x = poll();if (x != null)return x;else	// 没数据抛出异常throw new NoSuchElementException();
}

2.3.2 poll方法

// 拉取数据
public E poll() {final ReentrantLock lock = this.lock;lock.lock();	// 加锁try {// 若没有数据,直接返回null;否则执行dequeue,取出数据并返回return (count == 0) ? null : dequeue();} finally {lock.unlock();}
}
// 取出数据
private E dequeue() {// 将成员变量引用到局部变量final Object[] items = this.items;@SuppressWarnings("unchecked")E x = (E) items[takeIndex];		// 直接获取指定索引位置的数据items[takeIndex] = null;		// 取出数据后,清空该索引位置if (++takeIndex == items.length)	// 设置下次取数据的索引位置takeIndex = 0;count--;	// 数组中元素个数减一if (itrs != null)	// 迭代器内容先跳过itrs.elementDequeued();// signal方法,会唤醒当前Condition中排队的一个Node// signalAll方法,会将Condition中所有的Node,全都唤醒notFull.signal();return x;	// 返回数据
}

2.3.3 poll(timeout,unit)方法

public E poll(long timeout, TimeUnit unit) throws InterruptedException {long nanos = unit.toNanos(timeout);		// 转换时间单位final ReentrantLock lock = this.lock;lock.lockInterruptibly();				// 加锁,可中断唤醒try {while (count == 0) {	// 如果没数据if (nanos <= 0)		// 也没时间了,就不阻塞,返回nullreturn null;// 有时间,就挂起消费者线程一段时间nanos = notEmpty.awaitNanos(nanos);}return dequeue();	// 取数据} finally {lock.unlock();}
}

2.3.4 take方法

public E take() throws InterruptedException {final ReentrantLock lock = this.lock;lock.lockInterruptibly();try {while (count == 0)	// 使用while,防止虚假唤醒notEmpty.await();return dequeue();} finally {lock.unlock();}
}

2.3.5 虚假唤醒

阻塞队列中,如果需要线程挂起操作,判断有无数据的位置采用的是while循环,为什么不使用if?

  • 首先肯定不能换成 if 逻辑判断,比如:有线程 A、B、E、C,其中 ABE 是生产者,C是消费者。假如线程的队列是满的,AB挂起
// E,拿到锁资源,还没有走while判断
while (count == items.length)// A醒了// B挂起notFull.await();
enqueue(e)
  • C 此时消费一条数据,执行 notFull.signal() 唤醒一个线程,A线程被唤醒;E走判断发现有空余位置,可以添加数据到队列,则E添加数据,走enqueue。
  • 如果判断是 if,A 在E释放锁资源后,拿到锁资源,直接走 enqueue 方法,此时 A线程就是在 putIndex 的位置,覆盖掉之前的数据,会造成数据安全问题。

3 LinkedBlockingQueue

3.1 LinkedBlockingQueue的底层实现

  • 查看 LinkedBlockingQueue 是如何存储数据,以及如何实现链表结构的。
// Node对象就是存储数据的单位
static class Node<E> {// 存储的数据E item;// 指向下一个数据的指针Node<E> next;// 有参构造Node(E x) { item = x; }
}
  • 查看LinkedBlockingQueue的有参构造
// 可以手动指定LinkedBlockingQueue的长度,如果没有指定,默认为Integer.MAX_VALUE
public LinkedBlockingQueue(int capacity) {if (capacity <= 0) throw new IllegalArgumentException();this.capacity = capacity;// 在初始化时,构建一个item为null的节点,作为head和last,这种node可以成为哨兵Node,// 如果没有哨兵节点,那么在获取数据时,需要判断head是否为null,才能找next// 如果没有哨兵节点,那么在添加数据时,需要判断last是否为null,才能找nextlast = head = new Node<E>(null);
}
  • 查看LinkedBlockingQueue的其他属性
// 因为是链表,没有想数组的length属性,基于AtomicInteger来记录长度
private final AtomicInteger count = new AtomicInteger();
transient Node<E> head;	// 链表的头,用于取数据
private transient Node<E> last;	// 链表的尾,用于存数据
// 消费者的锁
private final ReentrantLock takeLock 

相关文章:

多线程与高并发——并发编程(4)

文章目录 四、阻塞队列1 基础概念1.1 生产者消费者概念1.2 JUC阻塞队列的存取方法2 ArrayBlockingQueue2.1 ArrayBlockingQueue的基本使用2.2 生产者方法实现原理2.2.1 ArrayBlockingQueue的常见属性2.2.2 add方法2.2.3 offer方法2.2.4 offer(time,unit)方法2.2.5 put方法2.3 消…...

设计模式之建造者模式

文章目录 盖房项目需求传统方式解决盖房需求传统方式的问题分析建造者模式概述是建造者模式的四个角色建造者模式原理类图建造者模式的注意事项和细节 盖房项目需求 需要建房子&#xff1a;这一过程为打桩、砌墙、封顶房子有各种各样的&#xff0c;比如普通房&#xff0c;高楼…...

源码编译安装opencv4.6.0,别的版本也行

1.下载opencv4.6.0 系统: ubuntu 1804 64位点我下载opencv 4.6.0 https://codeload.github.com/opencv/opencv/zip/refs/tags/4.6.0 指令下载 推荐: wget -O opencv.zip https://github.com/opencv/opencv/archive/4.6.0.zip wget -O opencv_contrib.zip https://github.com/…...

【MongoDB】Springboot中MongoDB简单使用

1. docker安装MongoDB 拉取镜像 docker pull mongo创建容器 docker run -di --name mongo-service --restartalways -p 27017:27017 -v ~/data/mongodata:/data mongo2. 导入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactI…...

Python 面试:单元测试unit testing 使用pytest

1. 对于函数进行单元测试 calc.py def add(x, y):"""Add Function"""return x ydef subtract(x, y):"""Subtract Function"""return x - ydef multiply(x, y):"""Multiply Function""…...

螺旋矩阵、旋转矩阵、矩阵Z字打印

螺旋矩阵 #include <iostream> #include <vector> void display(std::vector<std::vector<int>>&nums){for(int i 0; i < nums.size(); i){for(int j 0; j < nums[0].size(); j){std::cout<<nums[i][j]<< ;}std::cout<<…...

Seaborn绘制热力图的子图

Seaborn绘制热力图的子图 提示&#xff1a;如何绘制三张子图 绘制的时候&#xff0c;会出现如下问题 &#xff08;1&#xff09;如何绘制1*3的子图 &#xff08;2&#xff09;三个显示条&#xff0c;如何只显示最后一个 提示&#xff1a;下面就展示详细步骤 Seaborn绘制热力…...

C++二级题目4

小白鼠再排队 不会 多余的数 #include<iostream> #include<string.h> #include<stdio.h> #include<iomanip> #include<cmath> #include<bits/stdc.h> int a[2000][2000]; int b[2000]; char c[2000]; long long n; using namespace std; i…...

Tomcat 部署时 war 和 war exploded区别

在 Tomcat 调试部署的时候&#xff0c;我们通常会看到有下面 2 个选项。 是选择war还是war exploded 这里首先看一下他们两个的区别&#xff1a; war 模式&#xff1a;将WEB工程以包的形式上传到服务器 &#xff1b;war exploded 模式&#xff1a;将WEB工程以当前文件夹的位置…...

Delphi IdTcpServer IdTcpClient 传输简单文本

Delphi IdTcpServer IdTcpClient 传输简单文本 已经很久敲代码了&#xff0c;想找一段直接Delphi11 TCP简单文本传输&#xff0c;费劲&#xff01;FStringStream 、FStrStream &#xff1a; FStringStream:TStringStream.Create(,TEncoding.UTF8); 已经很久敲代码了&#xff0c…...

界面控件Telerik UI for WPF——Windows 11主题精简模式提升应用体验

Telerik UI for WPF拥有超过100个控件来创建美观、高性能的桌面应用程序&#xff0c;同时还能快速构建企业级办公WPF应用程序。Telerik UI for WPF支持MVVM、触摸等&#xff0c;创建的应用程序可靠且结构良好&#xff0c;非常容易维护&#xff0c;其直观的API将无缝地集成Visua…...

PoseC3D 基于人体姿态的动作识别新范式

摘要1. Introduction2. Related Work动作识别 3D-CNN基于骨架的动作识别 GCN基于骨骼的动作识别 2D-CNN3. Framework3.1. Good Practice for Pose Extraction3.2. From 2D Poses to 3D Heatmap Volumes3.3 基于骨骼的动作识别 3D-CNNPose-SlowOnlyRGBPose-SlowFast4. Experimen…...

html2canvas 截图空白 或出现toDataURL‘ on ‘HTMLCanvasElement或img标签没截下来 的所有解决办法

1.如果截图空白&#xff1a; 1.1以下的参数是必须要有的。 width: shareContent.offsetWidth, //设置canvas尺寸与所截图尺寸相同&#xff0c;防止白边height: shareContent.offsetHeight, //防止白边logging: true,useCORS: true,x:0,y:0,2&#xff0c;如果出现了报错 toData…...

Eclipse错误提示: Symbol ‘xxxx‘ could not be resolved

问题现象&#xff1a; 调试FPGA时&#xff0c;如果在qsys中增加新的内容&#xff0c;到nios中编译的时候就会提示找不到宏定义。 而这些宏定义都是在system.h这个头文件中的&#xff0c;原来的宏定义都能找到&#xff0c;就是新增的找不到&#xff0c;这个应该和头文件路径没有…...

基于Java的OA办公管理系统,Spring Boot框架,vue技术,mysql数据库,前台+后台,完美运行,有一万一千字论文。

基于Java的OA办公管理系统&#xff0c;Spring Boot框架&#xff0c;vue技术&#xff0c;mysql数据库&#xff0c;前台后台&#xff0c;完美运行&#xff0c;有一万一千字论文。 系统中的功能模块主要是实现管理员和员工的管理&#xff1b; 管理员&#xff1a;个人中心、普通员工…...

正则表达式(JAVA)

正则表达式(JAVA) 文章目录 正则表达式(JAVA)用法字符类(只匹配一个字符)预定义字符(只匹配一个字符)数量词贪婪爬取符号捕获分组规则捕获分组符号 非捕获分组案例忽略大小写 用法 正则表达式在用于校验信息是否满足某些规则的时候,非常的好用 在文本中查找满足要求的内容 字…...

264_BOOST中的Json库解析_BOOST_AUTO(itrpromodel, doc.FindMember(“productmodel“));

BOOST_AUTO(itrpromodel, doc.FindMember("productmodel"));if(itrpromodel != doc.MemberEnd()){BOOST_AUTO(iterd_url...

linux rpm 离线安装 nginx 自用,仅供参考

检查是否安装nginx ps -ef|grep nginx 检查rpm是否安装nginx rpm -qa|grep nginx 查看linux centos 发行版本 cat /etc/centos-release (查看其它发现版本 就把centos替换为别的 比如 红旗的 redflag ) 查看cpu信息 x86_64 lscpu 检查nginx所需依赖 …...

第十二章 YOLO的部署实战篇(上篇)

cuda教程目录 第一章 指针篇 第二章 CUDA原理篇 第三章 CUDA编译器环境配置篇 第四章 kernel函数基础篇 第五章 kernel索引(index)篇 第六章 kenel矩阵计算实战篇 第七章 kenel实战强化篇 第八章 CUDA内存应用与性能优化篇 第九章 CUDA原子(atomic)实战篇 第十章 CUDA流(strea…...

无涯教程-Android - List View函数

Android ListView 是垂直滚动列表中显示的视图&#xff0c;使用 Adapter 从列表(如数组或数据库)中获取内容的列表项会自动插入列表中。 适配器(Adapter)实际上是UI组件和将数据填充到UI组件中的数据源之间的桥梁&#xff0c;适配器保存数据并将数据发送到适配器视图&#xff0…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

C++实现分布式网络通信框架RPC(3)--rpc调用端

目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中&#xff0c;我们已经大致实现了rpc服务端的各项功能代…...

Flask RESTful 示例

目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题&#xff1a; 下面创建一个简单的Flask RESTful API示例。首先&#xff0c;我们需要创建环境&#xff0c;安装必要的依赖&#xff0c;然后…...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

拉力测试cuda pytorch 把 4070显卡拉满

import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试&#xff0c;通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小&#xff0c;增大可提高计算复杂度duration: 测试持续时间&#xff08;秒&…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”

目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域&#xff0c;向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能&#xff0c;能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作&#xff0c;并通过具体…...