自定义线程池 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

SpringBoot项目整合达梦数据库(MYSQL 转换 达梦数据库)

注意:当迁移过程中出现下面问题时,如有触发器或者外键,需要先关掉触发器和外键,然后进行迁移,不然会导致迁移失败。(2)dm.ini修改【#compatibility】的【COMPATIBLE_MODE】=4。3、添加用户:如果导入的DM数据库需要和MYSQL的用户一样,添加ROOT用户。方法一:服务器配置:设置系统兼容性(DM控制台工具),兼容MySQL。服务器配置又该有两种方式:①DM控制台工具修改;MYSQL的数据表转换成DM的数据表时,点击转换按钮。在这里操作:MYSQL数据库迁移至DM数据库。

m0_74824635的博客 2013

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

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

heouheou的博客 506

GX Works2系统启动失败排查指南:从环境配置到修复方案

在工业自动化与PLC编程调试中,软件启动异常是常见的工程痛点。当开发工具因系统环境配置不当而频繁报错时,往往不是简单的重启能解决的。运行库缺失、区域设置错误、临时目录失效或系统服务被禁用,都可能导致编程软件无法完成初始化流程。掌握从基础环境检查到系统组件修复的系统性排查思路,是提升工控开发效率的关键能力。本文从软件初始化原理出发,结合Windows系统维护实践,梳理一套适用于工业软件启动故障的通用诊断方法,并自然聚焦到三菱GX Works2常见启动失败问题的实际处理,帮助工程师快速定位并解决环境性故障,保障

weixin_42567046的博客 468

死磕GMSSL通信-java/Netty系列(二)

Netty-tcnative再linux编译比较好编译,但是再window编译,我尝试了各种办法,总是编译失败,所以我采用半自动的方式进行编译,Netty-tcnative会生成临时文件,直接用vs打开,设置openssl的头文件和静态库路径,然后生成静态库,静态库改个名字放到jar包里边,其实自动编译也是这个原理哈哈O(∩_∩)O。直接传输五个证书的路径吗,之前有的是说要转成x509之类的,转来转去的有点麻烦,这个是直接传输证书路径,由底层gmssl去解析,格式必须是pem格式。然而,若项目已依赖于。

qq_36577699的博客 3026

如何自定义线程池中队列

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

老男孩的架构路 1075

在Simulink中进行基于模拟信号数字化处理的仿真

模拟信号数字化处理通常包括以下几个步骤:采样、量化、编码以及数字信号处理(如滤波、变换等)。Simulink提供了丰富的模块库,可以方便地实现这些步骤,并支持对整个系统的实时仿真与调试。首先明确你的系统需求,比如输入信号类型(正弦波、方波等)、采样率、需要执行的数字信号处理操作(低通滤波、频谱分析等)。假设我们需要处理一个50Hz的正弦波信号,采样率为1kHz,并希望对该信号进行低通滤波以去除高频噪声。本文介绍了如何在Simulink中进行基于模拟信号数字化处理的仿真。

xiaoheshang_123的博客 626

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

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

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

基于单片机的智能体重秤设计与嵌入式程序实现

智能体重秤的基本功能是测量用户的体重,并将数据通过无线方式传输到终端设备(如智能手机、电脑或云平台)进行管理。体重测量:实时测量用户体重,并进行数据存储。BMI(体质指数)计算:基于体重和身高计算BMI值,帮助用户了解健康状况。多用户管理:通过蓝牙或Wi-Fi技术支持多用户体重数据记录。健康数据分析:结合历史数据分析体重变化趋势。语音播报:通过语音播报用户体重,便于老人或视力不便的人士使用。数据同步:通过App或云平台同步数据,进行长期健康监控。

2201_76125393的博客 1757

Python爬虫教程:抓取电商网站评论(以淘宝和京东为例)

通过本文的介绍,我们学会了如何使用Python编写爬虫,抓取淘宝和京东等电商平台的商品评论数据。我们介绍了如何使用电商平台提供的开放API以及如何通过模拟浏览器来抓取动态加载的数据。此外,我们还讨论了如何应对反爬虫技术,如何存储抓取的数据等。爬取电商平台的数据是一个强大的工具,能够为市场分析、消费者行为研究、产品优化等领域提供有力的支持。然而,在使用爬虫技术时,我们必须遵循法律法规,尊重平台的隐私政策,避免滥用数据。

2201_76125261的博客 1738

实现自定义线程池

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

Sumuxi9797926的博客 1161

利用恒源云在云端租用GPU服务器训练YOLOv8模型(包括Linux系统命令讲解)

为当涉及到深度学习的训练任务时,GPU的计算能力是不可或缺的。相对于传统的中央处理器(CPU),图形处理器(GPU)具有更强大的并行计算能力,能够显著加速深度学习模型的训练过程。深度学习算法通常涉及大量的矩阵运算和张量操作,而GPU的并行计算架构使得它们能够高效地执行这些计算,从而加速模型训练的速度。恒源云是一个经济高效的云计算平台,您可以通过恒源云的控制台或者命令行界面来管理实例、上传和下载数据、执行训练任务等。

Snu77的博客 6724

阻塞队列和线程池

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

qq_66292058的博客 1320

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

阻塞队列与线程池

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

徒破壁的博客 975

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

JUC-阻塞队列+线程池

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

BNMZY的博客 364

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

线程池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、付费专栏及课程。

余额充值