手写一个线程池
自己手动写一个线程池的必要条件需要先了解我们使用的线程池的功能。为什么会有线程池?这是为了减少线程创建和销毁的开销。复用线程的目的。为了达到这个目的。预计方案是:需要一个存放任务的队列,主线程相当于生产者,在这个队列里面push任务,启动几个核心线程一直消费这个队列,然后执行其中的任务。
1、线程池的几个重要参数
(1)corePoolSize 核心线程数 含义是一直存活的线程数
(2)maxPoolSize 最大线程数 含义是最大运行的线程个数
(3)keepaliveTime 存活的时间 超过核心线程数的线程存活时间
(4)unit 单位
(5)blockingQueue 阻塞队列 放置任务使用
(6)threadFactory 线程工厂
(7)拒绝策略:abort策略表示拒绝任务,但是会抛异常,discard策略表示拒绝任务,不会抛异常。CallerRunPolicy策略表示会使用调用者去执行任务。DiscardoldPolicy策略表示抛弃老的任务,将新的任务添加进队列
上述几种参数决定了线程池要对外暴露什么样的接口,精选1-5必要参数手动写一个线程池。
2、代码逻辑
我这边编写的步骤是:
1、先将参数定义好,构造方法定义出来,除了上述必要的1-5的参数外,还需要一个消费者工作集合。为什么要保存成集合呢?主要用于在取消的时候遍历其集合,然后进行中断。
2、编写submit方法逻辑,首先参数肯定是Runable任务,判断逻辑:
(1)当运行的工作线程小于核心线程,则直接启动一个工作线程然后加入线程集合中。
(2)当1不成立后,那就需要加入阻塞队列等待了,如果阻塞队列是数组类型阻塞队列,这时有范围限制,如果不超过,则加入队列。
(3)当阻塞队列超了,则查看当前线程个数是否小于最大线程个数。如果不超过,则启动一个工作线程消费其任务,并且加入其工作集合中
(4)如果超过最大线程数,实际上走拒绝策略逻辑,这里我们就直接报错。
3、上述工作线程内部怎么消费呢?因为是阻塞队列,自然就是循环队列中取任务然后执行。而这里的取任务,分成了poll和take。这是为什么?从上面步骤可知核心线程数超过之后也会存在启动工作线程的情况,那这些线程有一个保活时间,时间一到,只要此线程空闲,线程则会跳出循环结束执行。而这里阻塞队列的poll可以实现上述逻辑。take则在没有任务时就阻塞。
下面是按照其上述逻辑的代码,大家可以参考。
package org.example.Thread1;import java.util.HashSet;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;public class SelfThreadPoolExecutor {//一直存活的核心线程数private int corePoolSize;//运行存在的最大的线程数private int maxPoolSize;private AtomicInteger status = new AtomicInteger();private AtomicInteger workCount = new AtomicInteger();private final static Integer RUNNING = 0;private final static Integer STOP = 1;private int keepAliveTime;private BlockingQueue<Runnable> blockingQueue;private HashSet<Worker> hashSet = new HashSet<>();private ReentrantLock mainLock = new ReentrantLock();public SelfThreadPoolExecutor(int corePoolSize, int maxPoolSize, int keepAliveTime, BlockingQueue<Runnable> blockingQueue) {this.corePoolSize = corePoolSize;this.maxPoolSize = maxPoolSize;this.keepAliveTime = keepAliveTime;this.blockingQueue = blockingQueue;}public void submit(Runnable runnable) {if (status.get() == STOP) {throw new RuntimeException("不能添加新的任务了");}if (workCount.get() < corePoolSize && addWork(runnable, true)) {return;}if (blockingQueue.offer(runnable)) {return;}if (workCount.get() < maxPoolSize && addWork(runnable, false)) {return;}throw new RuntimeException("拒绝此任务");}public boolean addWork(Runnable runnable, boolean core) {if (status.get() == STOP) {return false;}while (true) {if (workCount.get() > (core ? corePoolSize : maxPoolSize)) {return false;}boolean inc = workCount.compareAndSet(workCount.get(), workCount.get()+1);if (!inc) {continue;}break;}mainLock.lock();try {Worker worker = new Worker(runnable);worker.thread.start();hashSet.add(worker);} finally {while (true) {boolean inc = workCount.compareAndSet(workCount.get(), workCount.get()-1);if (!inc) {continue;}break;}mainLock.unlock();}return true;}public void shutdown() {mainLock.lock();try {while (true) {if (status.get() == STOP) {break;}boolean stop = status.compareAndSet(status.get(), STOP);if (stop) {break;}}for (Worker worker : hashSet) {if (!worker.thread.isInterrupted()) {worker.thread.interrupt();}}}finally {mainLock.unlock();}}private final class Worker implements Runnable{Runnable firstTask;final Thread thread;public Worker(Runnable firstTask) {this.firstTask = firstTask;thread = new Thread(this);}@Overridepublic void run() {runWork(this);}public void runWork(Worker w) {Thread wt = w.thread;Runnable task = w.firstTask;w.firstTask = null;try {while (task != null || (task = getTask()) != null) {try {if (wt.isInterrupted()) {break;}if (status.get() == STOP) {break;}task.run();} finally {task = null;}}} finally {mainLock.lock();try {hashSet.remove(w);} finally {mainLock.unlock();}}}public Runnable getTask() {boolean timedOut = false;while (true) {try {if (status.get() == STOP) {return null;}if (timedOut) {return null;}Runnable task = null;if (workCount.get() > corePoolSize) {task = blockingQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);} else {task = blockingQueue.take();}if (task != null) {return task;}timedOut = true;} catch (InterruptedException e) {timedOut = true;}}}}
}
相关文章:
手写一个线程池
自己手动写一个线程池的必要条件需要先了解我们使用的线程池的功能。为什么会有线程池?这是为了减少线程创建和销毁的开销。复用线程的目的。为了达到这个目的。预计方案是:需要一个存放任务的队列,主线程相当于生产者,在这个队列…...
Spring Boot 多环境配置
Spring Boot 多环境配置 在现代的软件开发中,通常需要将应用程序部署到不同的环境中,如开发环境、生产环境和测试环境等。每个环境可能需要不同的配置参数,例如数据库连接信息、日志级别等。在 Spring Boot 中,我们可以通过简单的…...
【Python】一文带你详解sys.executable函数的作用
【Python】一文带你详解sys.executable函数的作用 🌈 个人主页:高斯小哥 🔥 高质量专栏:Matplotlib之旅:零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程👈 希望得到您的订阅和支…...
thingsboard如何自定义udp-transport
0、参考netty实现udp的文章 https://github.com/narkhedesam/Netty-Simple-UDP-TCP-server-client/blob/master/netty-udp/src/com/sam/netty_udp/server/MessageDecoder.java 调试工具使用的是:卓岚TCP&UDP调试工具 1、在common\transport下面创建udp模块,仿照mqtt的创…...
【汇编】#3 8086与数据有关的寻址方式
文章目录 操作码与操作数1. 8086处理器的与数据有关的寻址方式1.1 立即数寻址方式1.2 寄存器寻址方式 2. 有效(偏移)地址(effective address,EA)与缺省段寄存器选择tips:段跨越前缀2.1 直接寻址tips:直接寻址与立即寻址…...
【数据结构】单链表的层层实现!! !
关注小庄 顿顿解馋(●’◡’●) 上篇回顾 我们上篇学习了本质为数组的数据结构—顺序表,顺序表支持下标随机访问而且高速缓存命中率高,然而可能造成空间的浪费,同时增加数据时多次移动会造成效率低下,那有什么解决之法呢ÿ…...
丰田研究所(TRI)最新成果——可实现全身操控的软体机器人Punyo
文 | BFT机器人 人形机器人在近年的科技浪潮中迅速崛起,成为了各界瞩目的焦点,众多企业纷纷推出自家的机器人模型,但仔细观察,不难发现它们中的许多在操作方式上仍显得颇为相似。这些典型的人形机器人,以其机械臂和抓…...
【PyTorch实战演练】深入剖析MTCNN(多任务级联卷积神经网络)并使用30行代码实现人脸识别
文章目录 0. 前言1. 级联神经网络介绍2. MTCNN介绍2.1 MTCNN提出背景2.2 MTCNN结构 3. MTCNN PyTorch实战3.1 facenet_pytorch库中的MTCNN3.2 识别图像数据3.3 人脸识别3.4 关键点定位 0. 前言 按照国际惯例,首先声明:本文只是我自己学习的理解ÿ…...
MFC中字符串string类型和CString类型互转方法
在Microsoft Foundation Classes (MFC)中,CString是一个非常方便的类,用于处理C风格的字符串。有时,你可能需要在MFC的CString和C标准库中的std::string之间进行转换。下面是如何在两者之间进行转换的方法: CString转std::string…...
Jmeter-使用http proxy代理录制脚本
Jmeter-使用http proxy代理录制脚本 第1步:打卡jmeter工具新增1个线程组 第2步:给线程组添加1个HTTP请求默认值 第3步:设置下HTTP请求默认值第4步:在工作台中新增1个----HTTP代理服务器 第5步:设置HTTP代理服务器 …...
C++训练营:new 运算符
大家好: 衷心希望各位点赞。 您的问题请留在评论区,我会及时回答。 一、new 运算符 new 运算符用于动态分配一片内存空间,并返回这片内存空间的首地址,可将该首地址存入一个指针变量中,主要有以下三种格式。 二、格…...
C# 用Trace.WriteLine输出调试信息无法查看
写程序就会遇见BUG,这时候在代码不同部位输出一些标记的信息对查找错误非常有必要,一般情况下我们都是使用Console.WriteLine()方法来打印信息到控制台窗口,但有时候使用Console.WriteLine()方法会存在不方便的情况,比如鄙人遇到的…...
【Echarts】柱状图上方显示数字以及自定义值,标题和副标题居中,鼠标上显示信息以及自定义信息
欢迎来到《小5讲堂》 大家好,我是全栈小5。 这是《前端》系列文章,每篇文章将以博主理解的角度展开讲解, 特别是针对知识点的概念进行叙说,大部分文章将会对这些概念进行实际例子验证,以此达到加深对知识点的理解和掌握…...
HTML 语义化:构建优质网页的关键
🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…...
Flutter入门学习——Flutter和Dart
因为工作的需要,也为了个人发展,现在的话,转战Flutter跨端开发了,虽然目前的项目只发了android端,但是那天尝试了一下Ios的打包流程,也能运行,只是IOS那边的打包稍微复杂一些。 差不多学习了一…...
C++中的内存管理方式
一、C内存管理方式简介 C语言中的内存管理方式在C中可以继续使用,但是在有些地方就无能为力,而且使用起来比较麻烦。因此C中引入了自己的内存管理方式,通过new和delete操作符进行动态内存管理。 二、new语法 new可以申请1个或多个空间&…...
macos m1 arm芯片 使用jpype报错 FileNotFoundError: [Errno 2] JVM DLL not found
startJVM(jpype.getDefaultJVMPath()) 报错 Traceback (most recent call last):File "/Users/thomas990p/PycharmProjects/tuya/volcano-biz-scripts/WenKongFa/FinalCode/java2python/CallJavaAPI.py", line 12, in <module>startJVM(jpype.getDefaultJVMPa…...
Hive中UNION ALL和UNION的区别
1.概述 Hive官方提供了一种联合查询的语法,原名为Union Syntax,用于联合两个表的记录进行查询,此处的联合和join是不同的,join是将两个表的字段拼接到一起,而union是将两个表的记录拼接在一起。 换言之, jo…...
selenium高级应用
常见控件应用 复杂的控件操作1.操作Ajax选项2.滑动滑块操作 WebDriver的特殊操作元素class值包含空格property、attribute、text的区别定位动态id 截图功能页面截图页面截图,返回截图的二进制数据页面截图,返回base64的字符串截取指定元素。先定位元素&a…...
微信小程序重新加载当前页面、刷新当前页面
重新加载页面 使用wx.reLanuch(),url: 路径当前页面跳转, 页面所有数据重新初始化,已配置的数据不会保存 wx.reLaunch({url: /pages/orders/createOrder/createOrder, // 当前页面的路径}) reLanuch()的方法,会有一个…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...
centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...
涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...
【JavaWeb】Docker项目部署
引言 之前学习了Linux操作系统的常见命令,在Linux上安装软件,以及如何在Linux上部署一个单体项目,大多数同学都会有相同的感受,那就是麻烦。 核心体现在三点: 命令太多了,记不住 软件安装包名字复杂&…...
算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...
