线程池的理解以及实现线程池

线程池可以干什么:

  • 帮助我们减少线程的创建和销毁
  • 提高系统资源的利用率,同时控制并发执行的线程数量
  • 便于管理且提高响应速度

线程池的工作流程:

1.创建线程池

线程池的创建是通过 Executors 工厂方法或直接使用 ThreadPoolExecutor 构造函数来完成

使用 Executors 创建线程池的⼏种⽅式

  • newFixedThreadPool: 创建固定线程数的线程池
  • newCachedThreadPool: 创建线程数⽬动态增⻓的线程池.
  • newSingleThreadExecutor: 创建只包含单个线程的线程池
  • newScheduledThreadPool: 设定 延迟时间后执⾏命令,或者定期执⾏命令. 是进阶版的 Timer. Executors 本质上是 ThreadPoolExecutor 类的封装.

 直接使用ThreadPoolExecutor 构造函数来完成,代码的大致效果就是下面这段代码:

//使用 ThreadPoolExecutor 构造函数
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize,//核心线程数maximumPoolSize,//最大线程数keepAliveTime,//非核心线程数在空闲时存在的最长时间unit,//设置时间单位workQueue,//任务队列threadFactory,//线程工厂,可以自定义线程的创建过程,包括设置线程名称、优先级等(不是必须的)RejectedExecutionHandler///拒绝策略);

2.提交任务

一旦线程池创建完成,就可以向线程池提交任务。提交任务通常通过 ExecutorService 接口的 execute(Runnable command) 方法或使用 Lambda 表达式来完成。提交的任务会被封装成 RunnableCallable 对象。

使用 Lambda 表达式提交任务:

ExecutorService service = Executors.newFixedThreadPool(4);//创建一个固定大小为4的线程池service.execute(()->{System.out.println("你需要提交的任务");});

使用 Runnable 来提交任务:

ExecutorService service = Executors.newFixedThreadPool(4);//创建一个固定大小为4的线程池service.execute(new Runnable() {@Overridepublic void run() {System.out.println("你需要提交的任务");}});

3.检查当前线程数

  1. 如果正在运行的线程数小于corePoolSize (核心线程数)则创建新的线程来执行任务,即使当前线程池中有空闲线程
  2. 如果正在运行的线程数等于corePoolSize 且存在空闲线程,则使用空闲线程来执行任务
  3. 如果正在运行的线程数等于corePoolSize 但是没有空闲线程,则将任务加入任务队列中等待执行

4.检查任务队列

  1. 如果任务队列没有满,则将新提交的任务加入到队列中等待执行
  2. 如果队列已满,则根据当前线程数与 maximumPoolSize (最大线程数) 来判断:
  • 如果当前线程数小于maximumPoolSize,则创建新的线程来执行任务
  • 如果当前线程数等于maximumPoolSize 且任务队列已满,则触发拒绝策略
  • 如果当前线程数等于maximumPoolSize 但是任务队列没满,则将新的任务放入任务队列等待执行

5.执行拒绝策略

如果线程池无法接受更多的任务(即线程数量达到最大值且任务队列已满),则会根据RejectedExecutionHandler 实现来处理无法执行的任务

  • AbortPolicy: 抛出 RejectedExecutionException 异常。
  • CallerRunsPolicy: 由调用者线程执行新任务。(当前任务比较重要又不能被丢弃的时候就可以使用这个,但是可能会增加响应时间)
  • DiscardOldestPolicy: 丢弃队列中最老的任务。
  • DiscardPolicy: 丢弃新来的任务。

6.空闲线程的管理

我们可以理解成核心线程(corePoolSize)是公司里面的正式员工,非核心线程数(超过corePoolSize的部分)是公司里面的实习生,假设最大空闲时间(KeepAliveTime)为一个月,如果这一个月内正式员工没有任何工作(处于空闲状态),那么这种情况下是不会轻易被裁掉的(销毁),但是如果是实习生连续一个月没有工作,那么这种情况下是会面临被裁掉的风险(销毁)用于节约成本(资源)

如果非核心线程数大于 corePoolSize 且线程空闲时间超过 keepAliveTime ,则线程会被销毁,直到线程数等于 corePoolSize

7.关闭线程池

可以调用 ExecutorServiceshutdown() 方法来关闭线程池。这将阻止新的任务提交,但允许正在执行的任务完成。如果需要立即终止所有任务并关闭线程池,可以使用 shutdownNow() 方法。


代码实现线程池

使用 Executors 类工厂方法

newSingleThreadExecutor()

作用

  • 创建一个单线程化的 ExecutorService
  • 保证所有任务按照提交顺序执行,一次只执行一个任务

默认配置

  • corePoolSize: 1
  • maximumPoolSize: 1
  • keepAliveTime: 无意义(因为只有一个线程,且总是活跃的)
  • workQueueLinkedBlockingQueue(无界队列)
  • threadFactoryExecutors.defaultThreadFactory()
  • RejectedExecutionHandlerAbortPolicy()

代码示例:

public static void main(String[] args) {// 创建一个单线程的线程池ExecutorService service = Executors.newSingleThreadExecutor();//提交任务到线程池for(int i = 0;i<5;i++){final int id = i;service.submit(()->{System.out.println(Thread.currentThread().getName()+"正在执行任务"+id);});}}

运行结果:


newFixedThreadPool(int nThreads) 

作用:

  • 创建一个固定大小的线程池。
  • 线程池中的线程数量是固定的,一旦创建,线程池中的线程数量不会改变。
  • 适用于处理大量短期任务,且希望限制线程数量的情况。

默认配置

  • corePoolSizenThreads
  • maximumPoolSizenThreads
  • keepAliveTime: 无意义(因为线程池大小固定)
  • workQueueLinkedBlockingQueue(无界队列)
  • threadFactoryExecutors.defaultThreadFactory()
  • RejectedExecutionHandlerAbortPolicy()

代码示例:

public static void main(String[] args) {//创建一个固定大小为6的线程池ExecutorService service = Executors.newFixedThreadPool(6);//提交任务到线程池for(int i = 0;i<10;i++){final int id = i;service.submit(()->{System.out.println(Thread.currentThread().getName()+"正在执行任务"+id);});}}

运行结果:


newCachedThreadPool() 

作用:

  • 创建一个可以根据需要创建新线程的线程池。
  • 线程池会根据需要创建新线程,但会在任务完成后销毁空闲线程。
  • 适用于执行很多短期异步任务的场合。

默认配置

  • corePoolSize: 0
  • maximumPoolSizeInteger.MAX_VALUE
  • keepAliveTime: 60秒
  • workQueueSynchronousQueue(一个特殊的无界队列,队列本身不存储元素,每个插入操作必须等待另一个线程的移除操作)
  • threadFactoryExecutors.defaultThreadFactory()
  • RejectedExecutionHandlerAbortPolicy()

代码示例:

// 创建一个可缓存的线程池ExecutorService service = Executors.newCachedThreadPool();for(int i = 0;i<10;i++){final int id = i;service.submit(()->{System.out.println(Thread.currentThread().getName()+"正在执行任务"+id);});}

运行结果:


newScheduledThreadPool(int corePoolSize) 

作用:

  • 创建一个可以安排定期或延迟任务执行的线程池。
  • 线程池中的线程数量是固定的,可以根据需要扩展到 corePoolSize 的数量。
  • 适用于需要定期执行任务的场景。

默认配置

  • corePoolSizecorePoolSize
  • maximumPoolSizeInteger.MAX_VALUE
  • keepAliveTime: 无意义(因为线程池大小固定)
  • workQueueDelayedWorkQueue(一个特殊类型的队列,用于存放延迟任务)
  • threadFactoryExecutors.defaultThreadFactory()
  • RejectedExecutionHandlerAbortPolicy()

代码示例:

public static void main(String[] args) {//核心线程数为3ScheduledExecutorService service = Executors.newScheduledThreadPool(3);//给定的任务在指定延迟后执行一次service.schedule(()->{System.out.println("任务1执行");},5, TimeUnit.SECONDS);//每隔2秒执行一次,初始延迟0秒service.scheduleAtFixedRate(()->{System.out.println("周期性任务开始执行");},0,2,TimeUnit.SECONDS);}

运行结果:

使用 ThreadPoolExecutor 构造函数

演示一个简单的

public static void main(String[] args) {// 创建线程池ExecutorService executor = new ThreadPoolExecutor(2, // corePoolSize: 2 个正式员工5, // maximumPoolSize: 最多5个线程(2个正式员工 + 3个临时工)60, // keepAliveTime: 临时工空闲60秒后被销毁TimeUnit.SECONDS, // 时间单位为秒new ArrayBlockingQueue<>(10), // workQueue: 阻塞队列,最多容纳10个任务Executors.defaultThreadFactory(), // threadFactory: 使用默认的线程工厂new ThreadPoolExecutor.CallerRunsPolicy() // RejectedExecutionHandler: 由调用者线程执行新任务);}

自己实现一个简单的线程池

这么这段代码没有任何过多的考虑,只是单纯的实现了一个线程池

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;class MyThreadPool{private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();public void submit(Runnable runnable) throws InterruptedException {queue.put(runnable);}public MyThreadPool(int n){for(int i = 0;i<n;i++){Thread t = new Thread(()->{while(true){try {Runnable runnable = queue.take();runnable.run();} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();}}
}
public class demo1 {public static void main(String[] args) throws InterruptedException {MyThreadPool pool = new MyThreadPool(4);for(int i = 0;i<1000;i++){pool.submit(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+" hello");}});}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3268108.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

认证中心:基于cookie和session实现单点登陆

流程图 参数 不同域名之下&#xff08;不同父域名&#xff09; cookiesessionredis 流程追踪 用户访问系统1的受保护资源&#xff0c;系统1发现用户未登录&#xff0c;跳转至sso认证中心&#xff0c;并将自己的地址作为参数 sso认证中心发现用户未登录&#xff0c;将用户引…

arduino简要总述(控制LED及220V节能灯)

arduino简要总述&#xff08;控制LED及220V节能灯&#xff09; ArduinoArduinoIDE下载安装Arduino UNO R3 开发板介绍Atmel atmega 328微控制器端口数字输入输出端口端口0和端口1模拟输入端口模拟输出端口&#xff08;~11等&#xff09; 什么是数字信号及模拟信号&#xff1f;数…

大数据管理中心设计规划方案(可编辑的43页PPT)

引言&#xff1a;随着企业业务的快速发展&#xff0c;数据量急剧增长&#xff0c;传统数据管理方式已无法满足高效处理和分析大数据的需求。建立一个集数据存储、处理、分析、可视化于一体的大数据管理中心&#xff0c;提升数据处理能力&#xff0c;加速业务决策过程&#xff0…

【Android】使用ViewPager2与TabLayout实现顶部导航栏+页面切换

【Android】使用ViewPager2与TabLayout实现顶部导航栏&#xff0b;页面切换 TabLayout与ViewPager2概述 TabLayout TabLayout 是 Android 支持库中的一个组件&#xff0c;它是 Design 支持库的一部分。TabLayout 提供了一个水平的标签页界面&#xff0c;允许用户在不同的视图…

mysql触发器与存储过程练习

建立两个表:goods(商品表)、orders(订单表) 建立触发器&#xff0c;订单表中增加订单数量后&#xff0c;商品表商品数量同步减少对应的商品订单出数量,并测试 建立触发器&#xff0c;实现功能:客户取消订单&#xff0c;恢复商品表对应商品的数量 建立触发器&#xff0c;实现功…

Florence2:Advancing a unified representation for a variety of vision tasks

Florence-2模型:开启统一视觉基础模型的新篇章_florence -2-CSDN博客文章浏览阅读1.1k次,点赞108次,收藏109次。Florence-2是由微软Azure AI团队开发的一款多功能、统一的视觉模型。它通过统一的提示处理不同的视觉任务,表现出色且优于许多大型模型。Florence-2的设计理念是…

IAR使用调试详解

目录 1 IAR功能介绍 1.1 File文件菜单 1.2 Edit编辑菜单 1.3 View视图菜单 1.4 Projcet工程菜单 1.5Debug调试菜单 1.6 Disassembly反汇编菜单 1.7 Simulator下载调试工具 1.8 Tools工具菜单 1.9 Window窗口菜单 1.10 Help帮助菜单 2 IAR设置 2.1 插入/编辑模板 2…

【优秀python算法毕设】基于python时间序列模型分析气温变化趋势的设计与实现

1 绪论 1.1 研究背景与意义 在气候变化日益受到全球关注的背景下&#xff0c;天气气温的变化已经对人们的生活各方面都产生了影响&#xff0c;人们在外出时大多都会在手机上看看天气如何&#xff0c;根据天气的变化来决定衣物的穿着和出行的安排。[1]如今手机能提供的信息已经…

mysql中You can’t specify target table for update in FROM clause错误

mysql中You can’t specify target table for update in FROM clause错误 You cannot update a table and select directly from the same table in a subquery. mysql官网中有这句话&#xff0c;我们不能在一个语句中先在子查询中从某张表查出一些值&#xff0c;再update这张表…

项目总结:认证授权

文章目录 前言一、认证授权1.用户身份认证2.用户授权 二、业务流程1.统一认证2.单点登录3.第三方认证 二、Spring Security 认证1.Spring Security2.认证授权入门 前言 会议发布后用户通过页面进行查看。如何去记录用户的会议记录呢&#xff1f;要想掌握用户需要参会的情况就需…

【数据结构】栈(基于数组、链表实现 + GIF图解 + 原码)

Hi~&#xff01;这里是奋斗的明志&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f331;&#x1f331;个人主页&#xff1a;奋斗的明志 &#x1f331;&#x1f331;所属专栏&#xff1a;数据结构 &#x1f4da;本系列文章为个人学…

[OJ]水位线问题,1.采用回溯法(深度优先遍历求解)2.采用广度优先遍历求解

1.深度优先遍历 使用回溯法,深度优先遍历利用栈先进后出的特点,在加水控制水量失败时, 回到最近一次可对水进行加水与否的位置1.对于给定水量k,是否在[l,r]之间&#xff0c; 是:是否加水(加水y,用掉x,是否在[l,r]之间)(不加水y,用掉x,是否在[l,r]之间)先尝试加水&#xff0c;如…

AMQP-核心概念-3

本文参考以下链接摘录翻译&#xff1a; https://www.rabbitmq.com/tutorials/amqp-concepts 队列&#xff08;Queues&#xff09; AMQP 0-9-1模型中的队列和其他消息任务队列系统中的队列非常相似&#xff1a;它们用于存储被应用消费的消息。队列和交换机有一些相同的属性&…

js 习题 3

文章目录 绪论12345678910 求最长公共后缀111213 最大公约数1415结语 绪论 『虽有遗憾&#xff0c;绝不后悔。』—— 「古剑奇谭」 1 let buf"";process.stdin.on("readable",function(){let chunkprocess.stdin.read();if(chunk){bufchunk.toString();} …

Linux下git入门操作

0.创建仓库 可以按这个配置来&#xff0c;.gitignore中存放了上传时忽略的文件类型后缀。 1.clone仓库 在gitee上创建好仓库&#xff0c;点击克隆/下载&#xff0c; 复制地址fyehong/Linux_notes 。 在所需的文件夹中放置仓库。比如我在文件夹lesson9下存储仓库。就在less…

MySQL练手 --- 1141. 查询近30天活跃用户数

题目链接&#xff1a;1141. 查询近30天活跃用户数 思路&#xff1a; 题目要求&#xff1a;统计截至 2019-07-27&#xff08;包含2019-07-27&#xff09;&#xff0c;近 30 天的每日活跃用户数&#xff08;当天只要有一条活动记录&#xff0c;即为活跃用户&#xff09; 要计算…

AbutionGraph时序(流式)图数据库开发文档地址

AbutionGraph-时序(流式)图数据库&#xff0c;官方开发文档(API)地址&#xff1a; http://www.thutmose.cn

Java链接elasticsearch8.14.1

项目需求&#xff0c;需要实现海量数据的聚合、查询。因为职业生涯开发使用springboot微服务架构、Java开发的方式&#xff0c;所以&#xff0c;项目前期准备了elasticsearch、kibana、logstash的集群环境&#xff0c;作为服务端&#xff0c;用于数据的收集、存储&#xff1b;但…

『 Linux 』信号的写入与保存

文章目录 信号的发送信号的保存sigset_t 类型与信号集操作函数阻塞信号集(信号屏蔽字)操作函数未决信号集操作函数验证阻塞信号集与未决信号集 信号的发送 $ kill -l1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10)…

护眼台灯哪个牌子最好?五款平价护眼台灯推荐

如今在快节奏的生活里&#xff0c;不管是上班族还是作为一名学生党&#xff0c;都离不开长时间用眼的情况。高强度的用眼自然会对眼睛伤害大。近视加重可能都还算是小问题&#xff0c;严重的话会出现严重的眼部健康问题&#xff0c;所以&#xff0c;拥有一款合适的护眼台灯是很…