自定义线程池 01 - 阻塞队列

【面试:并发篇37:多线程:线程池】自定义线程池 建议学习本文章前最好对生产者消费者模式熟悉,可以看我之前的文章https这个图就是自定义线程池的实现结构,它是用生产者消费者模式实现的,它有三个部分ThreadPool、BlockingQueue、main组成。是线程池它的主要作用是进行线程的创建与任务的执行以及把超过线程池部分的任务交给主线程进行拒绝策略处理,我们可以把线程池中的线程当做消费者。main是任务的生产者,主要作用是任务的生产、线程池的创建、规定拒绝策略、执行拒绝策略。... 阅读详情

完整代码已上传gitee ,地址 :朱元杰的开源仓库 – ThreadPool核心源码仿写

完整文章栏目地址在:Fearless____的博客 - ThreadPool仿写

接下来将手动仿写一个线程池,第一步先仿写 阻塞队列

​​​​​​​​​​​​​​​​​在这里插入图片描述

为什么需要阻塞队列 - 因为不能为每个任务都创建一个线程,当任务数量超过可用线程的数量,需要将任务放在阻塞队列中

阻塞队列属性

阻塞队列我们定义为一个类 MyBlockingQueue ,要有如下几个属性

  • 任务队列 private Deque<T> queue = new ArrayDeque<>(); 使用 ArrayDeque 因为性能好于 LinkList
  • 锁 private ReentrantLock lock = new ReentrantLock(); 防止多个线程同时获取头部任务,也防止多个线程同时添加任务而发生线程安全问题
  • 生产者(main)条件变量 private Condition fullWaitSet = lock.newCondition(); 阻塞队列有容量限制,当任务过多,生产者线程需阻塞等待
  • 消费者(线程池的线程)条件变量 private Condition emptyWaitSet = lock.newCondition(); 当阻塞队列为空,消费者也需要阻塞等待
  • 容量 private int capcity; 阻塞队列的容量

任务添加逻辑

    // 阻塞添加
    public void put(T task) {
        lock.lock();
        try {
            while (queue.size() == capcity) {
                try {
                    log.debug("等待加入任务队列 {} ...", task);
                    fullWaitSet.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.debug("加入任务队列 {}", task);
            queue.addLast(task);
            emptyWaitSet.signal();
        } finally {
            lock.unlock();
        }
    }

上锁,while判断队列是否已满,满了则让当前 添加线程 进入 fullWaitSet.await() ,不满则执行 queue.addLast(task) 添加任务到队列,在此之前 线程池中的 可能有 消费线程 因为任务队列没有任务而进入 emptyWaitSet.await() ,因此添加完任务后有必要调用 emptyWaitSet.signal() 去唤他们

任务获取逻辑

    // 阻塞获取
    public T take() {
        lock.lock();
        try {
            while (queue.isEmpty()) {
                try {
                    emptyWaitSet.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            T t = queue.removeFirst();
            fullWaitSet.signal();
            return t;
        } finally {
            lock.unlock();
        }
    }

与任务添加的逻辑类似,不在赘述

阻塞优化 - 添加超时时间

只需将 await 方法替换为 awaitNanos 方法,就可以实现 带超时的阻塞添加和获取

带超时时间的阻塞获取如下

    // 带超时阻塞获取
    public T poll(long timeout, TimeUnit unit) {
        lock.lock();
        try {
            // 将 timeout 统一转换为 纳秒
            long nanos = unit.toNanos(timeout);
            while (queue.isEmpty()) {
                try {
                    // 返回值是剩余时间
                    if (nanos <= 0) {
                        return null;
                    }
                    nanos = emptyWaitSet.awaitNanos(nanos);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            T t = queue.removeFirst();
            fullWaitSet.signal();
            return t;
        } finally {
            lock.unlock();
        }
    }

给方法传入自定义的超时时间,toNanos 方法将时间统一转化为纳秒

emptyWaitSet.awaitNanos(nanos) 方法的返回值是 定义的等待时间nanos - 已等待的时间,因为存在虚假唤醒的可能

虚假唤醒 指 被唤醒的原因不是因为有新任务添加到阻塞队列中,也不是因为超时时间到,而是其他原因,因此唤醒后 阻塞队列中仍然可能为空,此时就要让他继续等待,不过只需等完剩余的超时时间,不能从头开始等待

如果超时时间耗尽,还没有新任务,就返回null,后面消费者线程获取null,就知道暂时没有任务需要执行,就会结束线程

带超时时间的阻塞添加如下

    // 带超时时间阻塞添加
    public boolean offer(T task, long timeout, TimeUnit timeUnit) {
        lock.lock();
        try {
            long nanos = timeUnit.toNanos(timeout);
            while (queue.size() == capcity) {
                try {
                    if (nanos <= 0) {
                        return false;
                    }
                    log.debug("等待加入任务队列 {} ...", task);
                    nanos = fullWaitSet.awaitNanos(nanos);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.debug("加入任务队列 {}", task);
            queue.addLast(task);
            emptyWaitSet.signal();
            return true;
        } finally {
            lock.unlock();
        }
    }

因为超时了就会失败,因此函数的返回值类型不能是void,应该设置为boolean,超时获取失败了就返回 false,其他逻辑与 超时阻塞获取 一致

JUC线程框架深度解析 — 01、JUC基础使用 一、JUC开发包简介【 java.util.concurrent开发包 】➣ 传统线程编程模型之中为防止死锁等现象的出现(wait()、notify()、   synchronized)时往往会考虑性能、公平性、资源管理等问题,这样加重了程序开发人员的负担;➣ Java5.0添加了一个新的java.util.concurrent开发包(简称JUC)。   利用此包进行的多线程编程将有效的减少竞争条... 阅读详情

相关推荐

Java线程池核心知识详解

线程池 线程池:三大方法、7大参数、4种拒绝策略 池化技术 程序的运行,本质:占用系统的资源!我们需要去优化资源的使用 ===> 池化技术 线程池、JDBC的连接池、内存池、对象池 等等。。。。 资源的创建、销毁十分消耗资源 池化技术:事先准备好一些资源,如果有人要用,就来我这里拿,用完之后还给我,以此来提高效率。 线程池的好处: 1、降低资源的消耗; 2、提高响应的速度; 3、方便管理; 4、线程复用、可以控制最大并发数、管理线程; 1、三大方法 //工具类 Executors 三大方法; p.

Free的午后 1119

自定义线程池的重要参数------阻塞队列

BlockingQueue阻塞队列接口,是一个支持两个附加操作的队列。在队列为空时,获取元素的线程会等待队列变为非空。当队列元素已满时,存储元素的线程会等待队列可用。阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是往队列里拿出元素的线程。阻塞队列的角色就是充当生产者存放元素的容器,而消费者只从容器里拿出元素,这也在一定程度上平衡了二者之间的处理能力,解决了它们之间的强耦合关系,在保证并发安全的同时,提高了队列的存取效率。

m0_64529944的博客 455

从零搭建ROS机械臂仿真环境:Ubuntu 20.04+Noetic下配置睿尔曼RM65的完整流程

本文详细介绍了在Ubuntu 20.04系统中,基于ROS Noetic搭建睿尔曼RM65机械臂的完整仿真环境。从环境准备、工作空间创建到MoveIt!配置与Gazebo仿真环境集成,提供了完整的流程和优化建议,帮助开发者高效实现机械臂仿真与调试。

v6b7n8m9q0的博客 933

java自定义实现阻塞队列和线程池

实际使用时可以把线程池定义为final static 类型,保证jvm中只存在一个;或者使用单例模式创建线程池。不多BB,直接上代码。

heouheou的博客 506

单片机按键识别篇---单击---双击----长按

  最近做一任务需要使用一个按键实现三种功能,分别是按键单击功能,按键双击功能和按键长按功能,可能之前没有接触过这类按键复用情况,顶多也只是简单识别单击和长按,没有想过双击以上按键功能,也是绞尽脑汁,想了半天,又上网看了些例程,算是对于按键的识别有点小小的了解,感觉这个功能挺实用,在此做个随笔。 一、思路   1.识别长按操作思路:我们使用系统定时器定时,然后让系统每过一段时间去扫描按键I/O...

weixin_30311605的博客 3660

Cartographer从安装到实战:手把手教你运行2D/3D建图与定位

本文提供了一份详尽的Cartographer从零安装到实战应用的完整指南。内容涵盖环境准备、依赖编译、源码构建等关键步骤,并手把手指导读者运行2D/3D官方数据集进行建图与定位测试。文章深入解析了参数配置与自定义数据适配方法,帮助开发者快速掌握这一强大的实时同步定位与建图工具,并将其应用于机器人导航与自动驾驶等实际场景。

rice5的博客 951

如何自定义线程池中队列

从图上我们可以看到,两个队列都继承了AbstractQueue实现了BlockingQueue接口,因此功能应该相似*task.SynchronousQueue类似于一个传递通道,只是通过他传递某个元素,并没有任何容量,只有当第一个元素被取走,才能在给队列添加元素。ArrayBlockingQueue从定义来看就是一个普通的队列,先入先出,当队列为空时,获取数据的线程会被阻塞,当队列满时,添加队列的线程会被阻塞,直到队列可用。...

老男孩的架构路 1075

TS808效果器制作教程

TS808效果器制作教程 可以在立创商城购买到大部分的电阻电容和IC,音频接口,踩钉,电位器和电源接口可以去某宝看看都买得到的。链接https//pan.baidu.com/s/1QId1eIE3fJ2u5NLBSISvGA。安装过程过于繁琐,把所有元器件按照图纸焊接完成就可以调试了,祝你们成功!这是TS808经典效果器制作过程的记录。IC是JRC4558。...

qq_18948149的博客 3427

【Java 并发编程】线程池机制 ( 线程池阻塞队列 | 线程池拒绝策略 | 使用 ThreadPoolExecutor 自定义线程池参数 )

一、线程池阻塞队列、 二、拒绝策略、 三、使用 ThreadPoolExecutor 自定义线程池参数、

让 学习 成为一种 习惯 ( 韩曙亮 の 技术博客 ) 1900

从Google到LLVM:5种主流clang-format预设风格横向对比(含代码对比图)

本文横向对比了LLVM、Google、Chromium、Mozilla和WebKit五种主流的clang-format预设代码风格。通过同一段C++代码的格式化效果对比图,直观展示了它们在指针对齐、大括号换行、缩进等关键细节上的差异,并提供了基于团队背景与项目基因的选型决策指南。

weixin_28828243的博客 444

使用R语言绘制Logistic回归模型的亚组分析森林图

亚组分析是一种常用的统计方法,用于探索不同亚组之间的差异,并确定是否存在某些变量对结果的调节作用。在本文中,我们将使用R语言来绘制Logistic回归模型的亚组分析森林图。在该图中,每个亚组都用一个点表示,点的位置表示亚组的均值,而线段表示亚组的置信区间。垂直参考线用于表示效应值为0的点,帮助我们判断亚组之间的差异。希望本文对你有所帮助,让你能够使用R语言绘制Logistic回归模型的亚组分析森林图。是一个二分类的变量,我们希望在亚组分析中探索它与响应变量之间的关系。函数来计算每个亚组的均值,并使用。

PixelNinja的博客 2003

实现自定义线程池

尝试着自己写一个线程池加深对JDK线程池的理解,从阻塞队列出发,协调任务的产生与线程池消费任务,用ReentrantLock锁技术,对阻塞队列是否已满,为空等情况进行阻塞挂起线程;再设计线程池,围绕任务的执行execute,与队列实现一些拒绝策略等等

Sumuxi9797926的博客 1161

SAP-RPT-1 深度解读:面向企业表格数据的 rapid one

摘要: SAP在TechEd 2025大会上推出面向企业结构化数据的SAP-RPT-1(Relational Pretrained Transformer),旨在通过in-context learning直接预测表格数据中的缺失值,无需传统机器学习流程。与LLM不同,RPT专为业务表格设计,支持实时预测(如交货延迟、回款风险等)。提供Small(高吞吐)、Large(高精度)和开源版本,用户可通过无代码Playground(上传CSV并用[PREDICT]标记目标列)或Python API快速验证。典型场景

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 236

阻塞队列和线程池

本篇将详细介绍阻塞队列和线程池。

qq_66292058的博客 1320

SpringBoot 系列教程(十二):给大家推荐20个SpringBoot开源项目,私活必备

基于 SpringBoot2.x + Shiro 实现了用户管理、个人微信、支付宝支付,内置扫码支付、后台回调、订单查询、限流等一系列功能

Thinkingcao的专栏 2839

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

阻塞队列与线程池

文章目录阻塞队列与线程池阻塞队列阻塞队列类型入队出队操作生产消费者模式传统版v1版本传统版v2版本阻塞队列v3版线程池线程池几种线程池线程池七大参数线程池运行过程线程池拒绝策略使用哪种线程池拒绝策略验证自定义线程池 阻塞队列与线程池 阻塞队列 阻塞队列类型 有七种类型的阻塞队列,但常用的线程池中主要用到三种阻塞队列,所以主要看看这三种阻塞队列。 ArrayBlockingQueue:由数组构成的...

徒破壁的博客 975

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

JUC-阻塞队列+线程池

文章目录BlockingQueue概念四组APISynchronousQueue同步队列代码线程池池化技术线程池创建三大方法AA BlockingQueue 概念 阻塞 队列间关系 使用阻塞队列的场景 多线程并发处理,线程池! 四组API 方式 抛出异常 有返回值 阻塞等待 超时等待 添加 add offer put offer(,) 移除 remove poll take poll(,) 检测队列首 element peek 抛出异常 /** * 抛出异常

BNMZY的博客 364

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

线程池01

线程池的创建 线程池的创建可以通过创建ThreadPoolExecutor对象或者调用Executors的工厂方法来创建线程池。但是在阿里巴巴的java开发手册中提到: [强制]线程池不允许使用Executors去创建,而是通过ThreadPoolExecutors的方式, 这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明: Executors 返回的线程池对象的弊端如下: 1.FixedThreadPool 和SingleThreadPool: 允许的请求队列长度为Integ

m0_51771769的博客 654

JUC中 阻塞队列和线程池

一、阻塞队列(blockQueue) 队列满的时候再放入和空了再取一定会发生阻塞! 父级是queue,和list,set等同级并继承父类collection。另dueue是双端队列,效率高,其中同步队列SynchronousQueue一次只有一个元素。不会造成阻塞队列的存入和取值策略: 方法 第一组会抛出异常 返回一个布尔值,不会抛出异常 延时等待 一直等待 插入 add() of...

qq_28945483的博客 180

java提高之线程池

阻塞队列 结构: Collection 接口 | Queue 接口 | BlockingQueue接口 | 实现类 ArrayBlockingQueue 底层数组 LinkedBlockingQueue 底层链表:默认长度21亿 SynchronousQueue 里面只能存放一个 阻塞队列常用API 抛出异常 特殊值 阻塞 超时 插入 add(e) offer(e)...

wjl31802的博客 1186

java线程池详解(01)

什么是线程池? 线程池:顾名思义就是事先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中获取线程不用自行创建,使用完毕不需要销毁线程,而是返回池中从而减少创建和销毁对象对CPU和内存的开销. 例如:线程池的概念类似人才资源中心,原本每次要运行一个线程(公司找一个员工)都必须去创建(培养)一个员工才行,有了线程池之后就在线程池中准备了多个创建好的线程(培...

weixin_41821522的博客 266

Java并发编程-线程池(01)

本篇文章主要是通过模拟实现自定义线程池来阐述Java并发编程中线程池的基础思想和基本实现方式。文章开篇介绍了池化思想的概念和线程池的重要性,接着通过模拟实现Java线程池来展示了线程池中的任务队列、超时设置、拒绝策略等知识点,同时也展示了一些拒绝策略的设计。

ikun的博客 1904

线程池的使用

线程池的使用

Krive~Irving 2178

7、线程池 线程池 常用的阻塞队列: 1、线程池的创建【重点】 2、ExecutorService的源码展示 3、线程池的工作流程、原理 4、饱和策略新提交 5、线程池的新API

LinkedBlockingQueue【链表】 阻塞队列中提供put()和take()方法【put添加、take获取】ThreadLocal是每个线程都独立创建的一个变量副本,可以用于避免多线程并发访问的安全问题。–线程池的新API【了解-newFixedThreadPool】–ArrayBlockingQueue【数组】–当take操作时,队列已空则会阻塞当前线程。–当put操作时,队列已满则会阻塞当前线程。......

Lantzruk的博客 305

通俗易懂理解线程池【01】

线程池

localhost65535的博客 749

Java线程池01之ThreadPoolExecutor

1. 介绍 用来创建线程池 2. 参数说明 参数 说明 corePoolSize 核心线程数 maximumPoolSize 最大线程数 keepAliveTime 超过corePoolSize,allowCoreThreadTimeOut(true)也可以设置核心线程数的超时时间 TimeUnit keepAliveTime时间单位 workQueue 阻塞任务队...

徐海兴的专栏 291
上一篇: ZyjDataLink 全量MySQL同步程序 - 开发过程 01
下一篇: 自定义线程池 02 - 初步实现线程池
Fearless____
博客等级 码龄5年 14粉丝 87原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值