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

手写一个线程池

自己手动写一个线程池的必要条件需要先了解我们使用的线程池的功能。为什么会有线程池?这是为了减少线程创建和销毁的开销。复用线程的目的。为了达到这个目的。预计方案是:需要一个存放任务的队列,主线程相当于生产者,在这个队列里面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;}}}}
}

 

 

相关文章:

手写一个线程池

自己手动写一个线程池的必要条件需要先了解我们使用的线程池的功能。为什么会有线程池&#xff1f;这是为了减少线程创建和销毁的开销。复用线程的目的。为了达到这个目的。预计方案是&#xff1a;需要一个存放任务的队列&#xff0c;主线程相当于生产者&#xff0c;在这个队列…...

Spring Boot 多环境配置

Spring Boot 多环境配置 在现代的软件开发中&#xff0c;通常需要将应用程序部署到不同的环境中&#xff0c;如开发环境、生产环境和测试环境等。每个环境可能需要不同的配置参数&#xff0c;例如数据库连接信息、日志级别等。在 Spring Boot 中&#xff0c;我们可以通过简单的…...

【Python】一文带你详解sys.executable函数的作用

【Python】一文带你详解sys.executable函数的作用 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448; 希望得到您的订阅和支…...

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. 有效&#xff08;偏移&#xff09;地址&#xff08;effective address&#xff0c;EA&#xff09;与缺省段寄存器选择tips:段跨越前缀2.1 直接寻址tips:直接寻址与立即寻址…...

【数据结构】单链表的层层实现!! !

关注小庄 顿顿解馋(●’◡’●) 上篇回顾 我们上篇学习了本质为数组的数据结构—顺序表&#xff0c;顺序表支持下标随机访问而且高速缓存命中率高&#xff0c;然而可能造成空间的浪费&#xff0c;同时增加数据时多次移动会造成效率低下&#xff0c;那有什么解决之法呢&#xff…...

丰田研究所(TRI)最新成果——可实现全身操控的软体机器人Punyo

文 | BFT机器人 人形机器人在近年的科技浪潮中迅速崛起&#xff0c;成为了各界瞩目的焦点&#xff0c;众多企业纷纷推出自家的机器人模型&#xff0c;但仔细观察&#xff0c;不难发现它们中的许多在操作方式上仍显得颇为相似。这些典型的人形机器人&#xff0c;以其机械臂和抓…...

【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. 前言 按照国际惯例&#xff0c;首先声明&#xff1a;本文只是我自己学习的理解&#xff…...

MFC中字符串string类型和CString类型互转方法

在Microsoft Foundation Classes (MFC)中&#xff0c;CString是一个非常方便的类&#xff0c;用于处理C风格的字符串。有时&#xff0c;你可能需要在MFC的CString和C标准库中的std::string之间进行转换。下面是如何在两者之间进行转换的方法&#xff1a; CString转std::string…...

Jmeter-使用http proxy代理录制脚本

Jmeter-使用http proxy代理录制脚本 第1步&#xff1a;打卡jmeter工具新增1个线程组 第2步&#xff1a;给线程组添加1个HTTP请求默认值 第3步&#xff1a;设置下HTTP请求默认值第4步&#xff1a;在工作台中新增1个----HTTP代理服务器 第5步&#xff1a;设置HTTP代理服务器 …...

C++训练营:new 运算符

大家好&#xff1a; 衷心希望各位点赞。 您的问题请留在评论区&#xff0c;我会及时回答。 一、new 运算符 new 运算符用于动态分配一片内存空间&#xff0c;并返回这片内存空间的首地址&#xff0c;可将该首地址存入一个指针变量中&#xff0c;主要有以下三种格式。 二、格…...

C# 用Trace.WriteLine输出调试信息无法查看

写程序就会遇见BUG&#xff0c;这时候在代码不同部位输出一些标记的信息对查找错误非常有必要&#xff0c;一般情况下我们都是使用Console.WriteLine()方法来打印信息到控制台窗口&#xff0c;但有时候使用Console.WriteLine()方法会存在不方便的情况&#xff0c;比如鄙人遇到的…...

【Echarts】柱状图上方显示数字以及自定义值,标题和副标题居中,鼠标上显示信息以及自定义信息

欢迎来到《小5讲堂》 大家好&#xff0c;我是全栈小5。 这是《前端》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对知识点的理解和掌握…...

HTML 语义化:构建优质网页的关键

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…...

Flutter入门学习——Flutter和Dart

因为工作的需要&#xff0c;也为了个人发展&#xff0c;现在的话&#xff0c;转战Flutter跨端开发了&#xff0c;虽然目前的项目只发了android端&#xff0c;但是那天尝试了一下Ios的打包流程&#xff0c;也能运行&#xff0c;只是IOS那边的打包稍微复杂一些。 差不多学习了一…...

C++中的内存管理方式

一、C内存管理方式简介 C语言中的内存管理方式在C中可以继续使用&#xff0c;但是在有些地方就无能为力&#xff0c;而且使用起来比较麻烦。因此C中引入了自己的内存管理方式&#xff0c;通过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官方提供了一种联合查询的语法&#xff0c;原名为Union Syntax&#xff0c;用于联合两个表的记录进行查询&#xff0c;此处的联合和join是不同的&#xff0c;join是将两个表的字段拼接到一起&#xff0c;而union是将两个表的记录拼接在一起。 换言之&#xff0c; jo…...

selenium高级应用

常见控件应用 复杂的控件操作1.操作Ajax选项2.滑动滑块操作 WebDriver的特殊操作元素class值包含空格property、attribute、text的区别定位动态id 截图功能页面截图页面截图&#xff0c;返回截图的二进制数据页面截图&#xff0c;返回base64的字符串截取指定元素。先定位元素&a…...

微信小程序重新加载当前页面、刷新当前页面

重新加载页面 使用wx.reLanuch&#xff08;&#xff09;&#xff0c;url: 路径当前页面跳转, 页面所有数据重新初始化&#xff0c;已配置的数据不会保存 wx.reLaunch({url: /pages/orders/createOrder/createOrder, // 当前页面的路径}) reLanuch()的方法&#xff0c;会有一个…...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分&#xff1a; 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析&#xff1a; CTR…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

云计算——弹性云计算器(ECS)

弹性云服务器&#xff1a;ECS 概述 云计算重构了ICT系统&#xff0c;云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台&#xff0c;包含如下主要概念。 ECS&#xff08;Elastic Cloud Server&#xff09;&#xff1a;即弹性云服务器&#xff0c;是云计算…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

人机融合智能 | “人智交互”跨学科新领域

本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)

前言&#xff1a; 在Java编程中&#xff0c;类的生命周期是指类从被加载到内存中开始&#xff0c;到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期&#xff0c;让读者对此有深刻印象。 目录 ​…...

NPOI Excel用OLE对象的形式插入文件附件以及插入图片

static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...

阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)

cd /home 进入home盘 安装虚拟环境&#xff1a; 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境&#xff1a; virtualenv myenv 3、激活虚拟环境&#xff08;激活环境可以在当前环境下安装包&#xff09; source myenv/bin/activate 此时&#xff0c;终端…...

Linux 下 DMA 内存映射浅析

序 系统 I/O 设备驱动程序通常调用其特定子系统的接口为 DMA 分配内存&#xff0c;但最终会调到 DMA 子系统的dma_alloc_coherent()/dma_alloc_attrs() 等接口。 关于 dma_alloc_coherent 接口详细的代码讲解、调用流程&#xff0c;可以参考这篇文章&#xff0c;我觉得写的非常…...

ui框架-文件列表展示

ui框架-文件列表展示 介绍 UI框架的文件列表展示组件&#xff0c;可以展示文件夹&#xff0c;支持列表展示和图标展示模式。组件提供了丰富的功能和可配置选项&#xff0c;适用于文件管理、文件上传等场景。 功能特性 支持列表模式和网格模式的切换展示支持文件和文件夹的层…...