线程池原理及改造
目录
一 线程池执行原理
二 线程池改造(一)
三 线程池改造(二)
一 线程池执行原理
首先我们先了解一下线程池里面几个参数:

第一个是核心线程数,第二个是线程池最大线程数。(线程池里面的线程分为核心线程和非核心线程,既然核心线程有3个,那么说明还可以创建最多2个非核心线程)
第三个是最大空闲时间,第四个是时间单位为秒,
第五个是阻塞队列(这个很重要),第六个是线程工厂,默认的,
最后一个是拒绝策略。
知道了每一个参数后,我们现在就来解释线程池的工作原理:

首先,当任务进入线程池后(这里假设线程池是正常运行状态,所以这个就不再做判断),线程池会先判断任务数量是否大于核心线程池的数量,如果不大于,那么就直接创建核心线程来执行任务,但是如果任务数量大于核心线程池数量,比如你只有三个核心线程池,但是你有四个任务,那么前三个任务会创建核心线程池去执行,而多出来的那个任务会放到阻塞队列里面,等核心线程空闲了再去执行阻塞队列里面的任务。
但是如果现在来了五个任务,那么,前三个交给核心线程池,第四个放在阻塞队列里面等待,而我们设置的阻塞队列只有一个,剩下的那个任务就会创建一个非核心线程去执行这个任务。
如果现在来了七个任务,前三个交给核心线程,第四个放在阻塞队列,第五和第六个会创建非核心线程执行,那么第七个呢?现在非核心线程也已经满了。那么这个时候就会有参数里面的拒绝策略去拒绝这个任务,抛异常处理等等。。。
二 线程池改造(一)
以上就是整个线程池执行原理,然后我们发现这里就会有个问题。就是当阻塞队列没满的时候,任务会等待核心线程执行完才会被执行,那如果核心线程执行的任务非常耗时那不是阻塞队列里面的任务就会一直等。
所以我们现在第一个改造,就是如果核心线程池占用完了,新的任务不是直接去阻塞队列等待,而是直接创建非核心线程去执行任务。
怎么改呢?
我们先看看ThreadPoolExecutor类里面的execute方法的代码:
public void execute(Runnable command) {if (command == null)throw new NullPointerException();int c = ctl.get();if (workerCountOf(c) < corePoolSize) {if (addWorker(command, true))return;c = ctl.get();}if (isRunning(c) && workQueue.offer(command)) {int recheck = ctl.get();if (! isRunning(recheck) && remove(command))reject(command);else if (workerCountOf(recheck) == 0)addWorker(null, false);}else if (!addWorker(command, false))reject(command);}

从上面的源码可以看见,在第三步的时候,它的判断如果为true,就会把任务放进阻塞队列,然后第四步才是创建非核心线程执行任务。
我们现在是想要先创建非核心线程直接执行任务,而不是先让任务进入阻塞队列,那么我们可以在第三步的判断条件中,然后的条件为false,那么它就不会进入阻塞队列,而是直接跳到下一步,创建非核心线程去执行任务。
那么我们只能重写workQueue.offer()方法了。
我们继承阻塞队列这个类,然后重写里面的offer方法,直接让他返回false:
package com.feisi.threadPool;import java.sql.Array;
import java.util.Collection;
import java.util.concurrent.ArrayBlockingQueue;public class TaskQueue<R extends Runnable> extends ArrayBlockingQueue<Runnable> {public TaskQueue(int capacity) {super(capacity);}public TaskQueue(int capacity, boolean fair) {super(capacity, fair);}public TaskQueue(int capacity, boolean fair, Collection<? extends Runnable> c) {super(capacity, fair, c);}@Overridepublic boolean offer(Runnable runnable) {
// 重写方法,直接返回falsereturn false ; }
}
然后我们把线程池的阻塞队列换成我们自己的:

看运行结果:

原先四个任务,前面三个有三个核心线程执行,后面一个在阻塞队列等待,直到核心线程执行完再去执行它,但是现在不一样了,任务4直接由非核心线程4去执行了。
三 线程池改造(二)
上面这个问题是解决了,我们加快了任务的执行速度,无需去等待核心线程执行完毕,就可以直接创建非核心线程去执行任务。
但是现在有一个问题,就是你直接返回false,相当于来一个任务,只要没超出最大线程数限制,你就直接给他创建一个非核心线程去执行,这样线程数多了容易影响性能,比如你来了四个任务,前三个由核心线程去执行,而第四个你就直接创建非核心线程了,那如果前面的核心线程已经执行完了呢?那是不是可以不需要创建非核心线程了,直接由空闲的核心线程去执行就行了,这样就可以大大减少线程的创建了。
所以我们应该再做一个判断,如果任务总数小于核心线程数,那么就由核心线程执行,如果大于核心线程数,那么就创建新的非核心线程去执行。
package com.feisi.threadPool;import java.sql.Array;
import java.util.Collection;
import java.util.concurrent.ArrayBlockingQueue;public class MyTaskQueue<R extends Runnable> extends ArrayBlockingQueue<Runnable> {private EagerThreadPoolExecutor threadPoolExecutor;public MyTaskQueue(int capacity) {super(capacity);}public MyTaskQueue(int capacity, boolean fair) {super(capacity, fair);}public MyTaskQueue(int capacity, boolean fair, Collection<? extends Runnable> c) {super(capacity, fair, c);}public void setThreadPoolExecutor(EagerThreadPoolExecutor threadPoolExecutor) {this.threadPoolExecutor = threadPoolExecutor;}@Overridepublic boolean offer(Runnable runnable) {//1.获取当前线程池的线程数量int currentPoolSize = threadPoolExecutor.getPoolSize();//如果当前任务数量小于核心线程数, 让核心线程数来执行// 有核心线程在空闲if(threadPoolExecutor.getSubmittedTaskCount() < threadPoolExecutor.getCorePoolSize()){
// System.out.println("111");//让核心线程数来执行return super.offer(runnable);}//如果当前线程池线程数量 < 最大线程池而且说明核心线程没有空闲那么就创建非核心线程执行。if(currentPoolSize < threadPoolExecutor.getMaximumPoolSize() ){return false;}//把任务存储到队列中return super.offer(runnable);}
}
还需要重写ThreadPollExector类的executor方法,来记录当前线程中需要处理的任务数:
package com.feisi.threadPool;import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;/*** 快速消费线程池*/
public class EagerThreadPoolExecutor extends ThreadPoolExecutor {public EagerThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);}public EagerThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);}public EagerThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);}public EagerThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);}private AtomicInteger submittedTaskCount = new AtomicInteger(0);//提供公开获取submittedTaskCount值public int getSubmittedTaskCount(){return submittedTaskCount.get();}@Overridepublic void execute(Runnable command) {
// System.out.println("进来了..");//任务数 + 1submittedTaskCount.incrementAndGet();try{Thread.sleep(500);super.execute(command);}catch (Exception e){ //拒绝//任务数-1submittedTaskCount.decrementAndGet();}}//表示任务执行完成的回调方法@Overrideprotected void afterExecute(Runnable r, Throwable t) {super.afterExecute(r, t);//任务数-1submittedTaskCount.decrementAndGet();}
}
运行结果:

可以看见,后面的线程也是由核心线程执行的,而没有去创建非核心线程,因为核心线程是空闲的,就不需要再去创建非核心线程了。
相关文章:
线程池原理及改造
目录 一 线程池执行原理 二 线程池改造(一) 三 线程池改造(二) 一 线程池执行原理 首先我们先了解一下线程池里面几个参数: 第一个是核心线程数,第二个是线程池最大线程数。(线程池里面的线程分为核心线程和非核心线程,既然核心…...
彻底理解mysql Buffer Pool (拓展)
彻底理解Buffer Pool (拓展) 一、Buffer Pool 的内存管理策略对数据库性能的影响 内存分配与回收:Buffer Pool 在申请内存时,需要考虑操作系统的内存分配策略。如果分配不合理,可能导致内存碎片,影响性能…...
信号量(二值信号量和计数信号量)和互斥量
信号量 信号量(Semaphore) 是一种实现任务间通信的机制, 可以实现任务之间同步或临界资源的互斥访问, 常用于协助一组相互竞争的任务来访问临界资源。 在多任务系统中, 各任务之间需要同步或互斥实现临界资源的保护&a…...
结构型模式-python版
在21种设计模式中, 结构型设计模式有7种, 分别是: 适配器模式代理模式桥接模式享元模式外观模式组合模式装饰器模式 下面逐一简要介绍: 1 适配器模式 适配器(Adapter)设计模式是一种结构型设计模式&…...
Java重修笔记 第五十四天 坦克大战(二)常用的绘图方法、画出坦克图形
常用的绘图方法 1.设置当前画笔的颜色,可多次调用 public abstract void setColor(Color c) 参数:c -颜色 2. 画一条直线 public abstract void drawLine(int x1, int y1, int x2, int y2) 参数:x1 - 第一个点的 x坐标。 y1 - 第一点的 y坐…...
OpenAI澄清:“GPT Next”不是新模型。
不,”GPT Next” 并不是OpenAI的下一个重要项目。 本周早些时候,OpenAI 日本业务的负责人长崎忠男在日本 KDDI 峰会上分享了一场演讲,似乎在暗示一个名为 “GPT Next” 的新模型即将出现。 但OpenAI的一位发言人已向Mashable证实࿰…...
<<编码>> 第 10 章 逻辑与开关(Logic and Switches) 示例电路
串联电路 info::操作说明 鼠标单击开关切换开合状态 需要两个开关同时闭合才能接通电路 primary::在线交互操作链接 https://cc.xiaogd.net/?startCircuitLinkhttps://book.xiaogd.net/code-hlchs-examples/assets/circuit/code-hlchs-ch10-01-series-circuit.txt 并联电路 in…...
深入浅出 Ansible 自动化运维:从入门到实战
在现代 IT 运维中,自动化是提升效率、降低错误率的关键。Ansible 作为一款流行的自动化工具,凭借其简洁的语法和强大的功能,成为了运维工程师的得力助手。本文将深入探讨 Ansible 的核心概念、实际应用以及一些实用的技巧,帮助你在…...
一句话描述设计模式
最近在看设计模式,其描述抽象程度令人欲罢不能,始终不得其意。于是尝试用一句话总结了一下,常规的就不说了,只是举了个例子。 单例模式 Spring中的单例bean使用了双重锁机制 工厂模式 Spring中的BeanFactory是简单工厂模式Bea…...
【Linux】Ubuntu 22.04 shell实现MySQL5.7 tar 一键安装
参考 https://blog.csdn.net/qq_35995514/article/details/134350572?spm1001.2014.3001.5501 在原作者基础上做了修改,加了一个删除原有mysql 的脚本 文章目录 一、安装下载**my.cnf 配置文件** 二、执行安装**install_mysql.sh 安装脚本**本机免密脚本 ssh_keyge…...
SQL Server开启网络访问
目前工作中很少用到SQL Server了,最近需要测试几个表,需要搭建一个SQL Server数据库服务,这里做个总结吧。 安装这里就不做详细介绍了,本文只介绍如何开启SQL Server网络访问。 1、云服务器安全组设置 如果是搭建在云服务器上&a…...
el-input设置type=‘number‘和v-model.number的区别
el-input设置typenumber’与设置.number修饰符的区别 1. 设置type‘number’ 使用el-input时想收集数字类型的数据,我们首先会想到typenumber,设置完type为number时会限制我们输入的内容只能为数字,不能为字符/汉字等非数字类型的数值&…...
6.第二阶段x86游戏实战2-理解程序流程
免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 本次游戏没法给 内容参考于:微尘网络安全 工具下载: 链接:https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd6tw3 提…...
Netty笔记01-Netty的基本概念与用法
文章目录 1. 概述1.1 Netty 是什么?1.2 Netty 的特点1.3 Netty 的作者1.4 Netty 的地位1.5 Netty 的优势1.6 Netty 的工作原理1.7 Netty 的应用场景1.8 Netty 的重要组件 2. 第一个程序2.1 目标2.2 服务器端2.3 客户端2.4 流程梳理💡 提示 1. 概述 1.1 …...
OpenHarmony鸿蒙( Beta5.0)RTSPServer实现播放视频详解
鸿蒙开发往期必看: 一分钟了解”纯血版!鸿蒙HarmonyOS Next应用开发! “非常详细的” 鸿蒙HarmonyOS Next应用开发学习路线!(从零基础入门到精通) “一杯冰美式的时间” 了解鸿蒙HarmonyOS Next应用开发路…...
QT使用事件事件和绘制事件实现简易时钟
这个时钟实现的底层原理主要是利用 Qt 的绘图机制和定时器。首先,设置固定大小的窗口,创建定时器并连接到槽函数,定时器每秒钟触发一次,触发窗口重绘。在paintEvent函数中,使用QPainter进行绘图,绘制圆形表…...
kubeadm方式安装k8s
一、安装环境 环境准备:(有阿里云)centos7 k8s-master 192.168.1.11 k8s-node1 192.168.1.22 k8s-node2 192.168.1.33 二、前期准备 在k8s-master主机 [rootk8s-master ~]# vim /etc/hosts…...
如何使用go生成可执行文件
在 Go 中生成可执行文件非常简单。你可以使用 go build或者go install 命令。以下是步骤: 1. 步骤 1.1. 打开终端,导航到你的 Go 项目目录 确保你在包含 main 包的目录中,通常这是项目的根目录或包含 main.go 文件的目录。 1.2. 运行 go …...
手写Promise
构造器的实现 const PENDING pending; const FULFILLED fulfilled; const REJECTED rejectedclass MyPromise{#state PENDING;#result undefined;constructor(executor){const resolve (data) > {this.#changeState(FULFILLED, data);};const reject (reason) > …...
深度学习云服务器免费使用教程
#云服务器# #深度学习# #人工智能# #计算机视觉# 本文为各位学习深度学习的入门选手而创建,降低深度学习的入门门槛。 谷歌云服务器Colab: T4GPU。限额,需要科学上网,不能使用终端。 谷歌云服务器地址:欢迎使用 C…...
量子计算如何革新机器翻译:QEDACVC系统解析
1. 量子计算与机器翻译的技术融合量子计算正在为自然语言处理领域带来革命性的变化。传统机器翻译系统依赖于经典计算机架构,如基于Transformer的模型,虽然取得了显著进展,但在处理低资源语言和实时多语言场景时仍面临挑战。量子机器翻译的核…...
5分钟搭建拼多多商品数据采集系统:电商从业者的完整解决方案
5分钟搭建拼多多商品数据采集系统:电商从业者的完整解决方案 【免费下载链接】scrapy-pinduoduo 拼多多爬虫,抓取拼多多热销商品信息和评论 项目地址: https://gitcode.com/gh_mirrors/sc/scrapy-pinduoduo 在电商竞争日益激烈的今天,…...
保姆级教程:在CentOS 7上用极简包5分钟搞定openGauss数据库安装
5分钟极速部署:CentOS 7下openGauss数据库极简安装实战 当开发进度紧迫时,一个能快速搭建的数据库环境往往能挽救整个项目的时间线。本文将带您用官方极简安装包,在CentOS 7系统上5分钟内完成openGauss数据库的部署。这种方法特别适合需要立即…...
告别折腾:用 apt 和 Qt 官方安装器两种方式在 Debian 上搞定 Qt 5.15.2 开发环境
在 Debian 上搭建 Qt 5.15.2 开发环境的双轨方案 对于需要在 Debian 系统上建立 Qt 开发环境的工程师来说,选择正确的安装方式往往比安装本身更重要。本文将深入探讨两种主流方案:Debian 官方仓库的 apt 安装和 Qt 官方在线安装器,帮助您根据…...
3个步骤让你的Mac原生支持200+视频格式预览
3个步骤让你的Mac原生支持200视频格式预览 【免费下载链接】QuickLookVideo This package allows macOS Finder to display thumbnails, static QuickLook previews, cover art and metadata for most types of video files. 项目地址: https://gitcode.com/gh_mirrors/ql/Qu…...
从ColorDialog到FontDialog:手把手教你定制WinForm功能对话框,打造个性化桌面应用
从ColorDialog到FontDialog:WinForm功能对话框的深度定制与用户体验优化 在桌面应用开发中,对话框不仅是功能实现的工具,更是用户体验的重要组成部分。想象一下,当用户在使用你的文本编辑器时,能够像专业软件那样流畅地…...
DWC_ether_qos驱动软复位实战:解决网络丢包与DMA死锁
1. 项目概述:从一次诡异的网络丢包说起最近在调试一块基于某款主流SoC的工控板卡时,遇到了一个让人头疼的问题:设备在长时间高负载运行后,网络会间歇性地出现严重丢包,甚至完全断连。重启网络服务能暂时恢复࿰…...
vue-pdf踩坑实录:从‘Cannot read properties of undefined’到完美预览的避坑指南
Vue-PDF实战避坑指南:从版本冲突到性能优化的全链路解决方案 1. 当控制台抛出"undefined catch"错误时 那个令人窒息的红色报错框突然出现在控制台——"Cannot read properties of undefined (reading catch)"。作为经历过三次类似场景的老手&a…...
【免费下载】 新概念英语第三册资源集合
新概念英语第三册资源集合 【下载地址】新概念英语第三册资源集合 新概念英语第三册资源集合 项目地址: https://gitcode.com/open-source-toolkit/8a5ad 资源介绍 本仓库提供了一系列新概念英语第三册(New Concept English 3)的资源文件&#x…...
LeetCode热题100-从前序与中序遍历序列构造二叉树
给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。 示例 1: 输入: preorder [3,9,20,15,7], inorder [9,3,15,20,7] 输出: [3,9,20,null,null,15,7] 思…...
