JavaEE之线程池
前面我们了解了多个任务可以通过创建多个线程去处理,达到节约时间的效果,但是每一次的线程创建和销毁也是会消耗计算机资源的,那么我们是否可以将线程进阶一下,让消耗计算机的资源尽可能缩小呢?线程池可以达到此效果,今天我们对 《线程池》 一探究竟!
1. 何为线程池?
线程池(Thread Pool)是一种并发编程的技术,用于在应用程序启动时创建一定数量的线程,并将它们保存在线程池中,以便于任务的执行和线程的重用。当有新任务到来时,线程池会分配一个空闲线程来执行该任务,任务完成后,线程不会被销毁,而是返回线程池中,等待执行下一个任务。
2. 为什么要用线程池?
为什么要用线程池,那就得从线程池的优点来说了
优点:
- 重用线程:避免了频繁创建和销毁线程的开销,提高了线程的利用率。
- 控制并发度:可以限制并发执行的线程数量,防止系统过载。
- 提供线程管理和监控:方便开发人员进行线程的管理和调试。
- 提供任务队列:实现任务的缓冲和调度,提升系统响应速度。
3. 为什么使用线程池可以提升效率?
由于线程的频繁创建和销毁会影响计算机效率,所以我们可以减少线程的频繁创建和销毁来提高计算机效率,因为线程池可以实现这种效果,所以可以提升效率,因为线程池做的是少量创建,少量销毁,进而做到了提升效率的功能
线程池最⼤的好处就是减少每次启动、销毁线程的损耗。
从另一种方面来说,计算机分为 内核态(操作系统层面) 和 用户态JVM层面(应用程序层),创建和销毁线程是内核态的操作
4. 线程池怎么用?
Java当中JDK给我们提供了一组针对不同使用场景的线程池实例,如下:
//1.用来处理大量短时间工作任务的线程池,如果池中没有可用的线程将创建新的线程,如果线程空闲60秒将回收并移除缓存
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
//2.创建一个操作无界队列且固定大小线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
//3.创建一个操作无界队列且只有一个工作线程的线程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
//4.创建一个单线程执行器,可以在给定时间后执行或定期执行
ScheduledExecutorService singleThreadScheduledExecutor=Executors.newSingleThreadScheduledExecutor();
//5.创建一个指定大小的线程池,可以在给定时间后执行或定期执行
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
//6.创建一个指定大少(不传入参数,为当煎机器CPU核心数)的线程池,并行地处理任务
Executors.newWorkStealingPool();
说明:
- 使用Executors.newFixedThreadPool(3)能创建出固定包含3个线程的线程池.
- newCachedThreadPool:创建线程数目动态增长的线程池.
- newFixedThreadPool:创建固定线程数的线程池 ,返回值类型为ExecutorService.
- newSingleThreadExecutor: 创建只包含单个线程的线程池.
- newSingleScheduledThreadPool:创建一个单线程执行器,设定延迟时间后执行命令,或者定期执行命令,是进阶版的Timer.
- newScheduledThreadPool:创建一个指定大小的线程池,设定延迟时间后执行命令,或者定期执行命令.是进阶版的Timer.
- 无界:任务队列的容量 没有限制称为无界队列
- Executors本质上是ThreadPoolExecutor类的封装.


5. 自我实现一个线程池
思路:
- 首先要描述任务
通过Runnable描述任务
- 通过一个阻塞队列管理任务
//阻塞队列组织任务,最多处理100个任务
public static BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(100);
- 提供一个方法向线程池提交任务
//添加任务到阻塞队列当中public void submit(Runnable runnable) throws InterruptedException {if(runnable==null){throw new IllegalAccessError("任务为空");}queue.put(runnable);}
- 创建多个线程,不停的扫描队列中的任务
//threadNum为初始化时,给定的线程池中含有线程的个数
public MyThreadPool(int threadNum) {if(threadNum<=0){throw new RuntimeException("线程数量必须大于0");}for (int i = 0; i < threadNum; i++) {Thread thread=new Thread(()->{//不停的扫描线程while (true) {try {//从线程中取出任务Runnable runnable = queue.take();//执行任务runnable.run();} catch (InterruptedException e) {e.printStackTrace();}}});//启动线程thread.start();}}
- 测试我们的线程池是否成功
public class Test {public static void main(String[] args) throws InterruptedException {//创建线程数量为3的线程池MyThreadPool myThreadPool=new MyThreadPool(3);//向线程池提交10个任务for (int i = 0; i < 10; i++) {int taskId=i+1;myThreadPool.submit(new Runnable() {@Overridepublic void run() {System.out.println("执行任务"+taskId+", "+Thread.currentThread().getName());}});}}
}
- 完整的自我线程池如下:
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;public class MyThreadPool {//阻塞队列组织任务,最多处理100个任务public static BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(100);//添加任务到阻塞队列当中public void submit(Runnable runnable) throws InterruptedException {if(runnable==null){throw new IllegalAccessError("任务为空");}queue.put(runnable);}public MyThreadPool(int threadNum) {if(threadNum<=0){throw new RuntimeException("线程数量必须大于0");}for (int i = 0; i < threadNum; i++) {Thread thread=new Thread(()->{//不停的扫描线程while (true) {try {//从线程中取出任务Runnable runnable = queue.take();//执行任务runnable.run();} catch (InterruptedException e) {e.printStackTrace();}}});//启动线程thread.start();}}
}
6. 创建系统自带的线程池

执行流程:
- 添加任务,核心线程从队列中取任务去执行
- 核心线程都在工作时,再添加的任务会进入到阻塞队列
- 阻塞队列满了后,会创建临时线程
- 阻塞队列满了并且临时线程达到最大,执行拒绝策略
一次性创建最大的线程数(根据机器的配置,比如CPU核数)
图解:

说明:
- corePoolSize: 核心线程的数量.(类似于正式员⼯,⼀旦录⽤,永不辞退)
- maximumPoolSize:线程池中最大的线程数=核心线程+临时线程的数目.(类似于临时⼯:⼀段时间不⼲活,就被辞退)
- keepAliveTime:临时线程存活的时间
- unit:临时线程存活的时间单位,是秒,分钟,还是其他值
- workQueue:组织(保存)任务的队列
- threadFactory: 创建线程的工厂,参与具体的创建线程⼯作.通过不同线程工厂创建出的线程相当于对一些属性进行了不同的初始化设置
- RejectedExecutionHandler: 拒绝策略,如果任务量超出线程池的负荷了接下来怎么处理

接下来我们探讨一下这四种拒绝策略:
1. AbortPolicy(): 超过负荷,直接抛出异常.
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class TestAbortPolicy {public static void main(String[] args) throws InterruptedException {ThreadPoolExecutor reject1 =new ThreadPoolExecutor(3,5,1,TimeUnit.SECONDS,new LinkedBlockingQueue<>(10),new ThreadPoolExecutor.AbortPolicy());for (int i = 0; i < 100; i++) {int taskId = i + 1;reject1.submit(() -> {System.out.println("执行任务:" + taskId + ", " + Thread.currentThread().getName());});}}
}

举个例子: 现在我在和我的女友玩游戏,老师让我帮他做一项报告,我直接拒绝了,并抛出异常,我不做!!
2. CallerRunsPolicy():返回给调⽤者负责处理多出来的任务.
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class TestCallerRunsPolicy {public static void main(String[] args) {ThreadPoolExecutor reject2=new ThreadPoolExecutor(3,5,1,TimeUnit.SECONDS,new LinkedBlockingQueue<>(10),new ThreadPoolExecutor.CallerRunsPolicy());for (int i = 0; i < 100; i++) {int taskId = i + 1;reject2.submit(()->{System.out.println("执行任务:" + taskId + ", " + Thread.currentThread().getName());});}}
}

举个例子: 现在我在和我的女友玩游戏,老师让我帮他做一项报告,我直接拒绝了,老师就自己去做了,保证了任务至少完成了。
3. DiscardOldestPolicy():丢弃队列中最⽼的任务.
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class TestDiscardOldestPolicy {public static void main(String[] args) {ThreadPoolExecutor reject2=new ThreadPoolExecutor(3,5,1,TimeUnit.SECONDS,new LinkedBlockingQueue<>(10),new ThreadPoolExecutor.DiscardOldestPolicy());for (int i = 0; i < 100; i++) {int taskId = i + 1;reject2.submit(()->{System.out.println("执行任务:" + taskId + ", " + Thread.currentThread().getName());});}}
}

举个例子: 现在我在和我的女友玩游戏,老师让我帮他做一项报告,我直接退出游戏(不处理最老的任务),去完成老师交给我的任务。
4. DiscardPolicy():丢弃新来的任务.
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class TestDiscardPolicy {public static void main(String[] args) {ThreadPoolExecutor reject2=new ThreadPoolExecutor(3,5,1,TimeUnit.SECONDS,new LinkedBlockingQueue<>(10),new ThreadPoolExecutor.DiscardPolicy());for (int i = 0; i < 100; i++) {int taskId = i + 1;reject2.submit(()->{System.out.println("执行任务:" + taskId + ", " + Thread.currentThread().getName());});}}
}

举个例子: 现在我在和我的女友玩游戏,老师让我帮他做一项报告,我直接拒绝了,老师也不做,日后可能忘记了该任务,再也找不回来。
总结: 在实际的开发过程中,我们要根据实际的业务场景选择不同的拒绝策略!!!
以上就是线程池的基本知识了,希望对你有帮助,谢谢!!
相关文章:
JavaEE之线程池
前面我们了解了多个任务可以通过创建多个线程去处理,达到节约时间的效果,但是每一次的线程创建和销毁也是会消耗计算机资源的,那么我们是否可以将线程进阶一下,让消耗计算机的资源尽可能缩小呢?线程池可以达到此效果&a…...
java 中 main 方法使用 KafkaConsumer 拉取 kafka 消息如何禁止输出 debug 日志
pom 依赖: <dependency><groupId>org.springframework.kafka</groupId><artifactId>spring-kafka</artifactId><version>2.5.14.RELEASE</version> </dependency> 或者 <dependency><groupId>org.ap…...
【后端面试总结】Golang可能的内存泄漏场景及应对策略
Golang可能的内存泄漏场景及应对策略 一、引言 Golang作为一种高性能、并发友好的编程语言,其内置的垃圾回收机制极大地简化了内存管理。然而,这并不意味着开发者可以完全忽视内存泄漏问题。在实际开发中,由于不当的资源管理、循环引用、以…...
Java 反射机制详解
在 Java 编程世界中,反射机制犹如一把神奇的钥匙,它能够打开许多隐藏在代码深处的 “大门”,让开发者突破常规的限制,实现一些极具灵活性的功能。今天,就跟随我一同深入探究 Java 反射机制的奥秘。 一、什么是反射 反…...
【k8s】scc权限 restricted、anyuid、privileged
文章目录 概述1. 内置的scc2. OpenShift如何确定pod的scc2.1 Pod未带SCC标签的情况2.2. Pod带有SCC标签的情况 参考 概述 在OpenShift(后文简称OCP)中,很早就一个概念:Security Context Constraints ,简称SCC…...
2025华数杯国际赛A题完整论文讲解(含每一问python代码+数据+可视化图)
大家好呀,从发布赛题一直到现在,总算完成了2025“华数杯”国际大学生数学建模竞赛A题Can He Swim Faster的完整的成品论文。 本论文可以保证原创,保证高质量。绝不是随便引用一大堆模型和代码复制粘贴进来完全没有应用糊弄人的垃圾半成品论文…...
ThreadLocal 的使用场景
在现代电商平台中,ThreadLocal 常用于以下场景,特别是与线程隔离相关的业务中,以提高性能和简化上下文传递。 1. 用户上下文信息管理 场景:在用户发起的每次请求中,需要携带用户 ID、角色、权限等信息,而这…...
后端开发 Springboot整合Redis Spring Data Redis 模板
目录 redis 配置 RedisConfig 类 完整代码 代码讲解 1. 类定义和注解 2. 定义 RedisTemplate Bean 3. 配置 JSON 序列化 4. 配置 Redis 的 key 和 value 序列化方式 5. 完成配置并返回 RedisTemplate 总结 redis 服务接口实现类 类级别 注入 RedisTemplate 常用 Re…...
代码随想录算法训练营第 4 天(链表 2)| 24. 两两交换链表中的节点19.删除链表的倒数第N个节点 -
一、24. 两两交换链表中的节点 题目:24. 两两交换链表中的节点 - 力扣(LeetCode) 视频:帮你把链表细节学清楚! | LeetCode:24. 两两交换链表中的节点_哔哩哔哩_bilibili 讲解:代码随想录 dummy-…...
【RDMA学习笔记】1:RDMA(Remote Direct Memory Access)介绍
从帝国理工的PPT学习。 什么是RDMA Remote Direct Memory Access,也就是Remote的DMA,是一种硬件机制,能直接访问远端结点的内存,而不需要处理器介入。 其中: Remote:跨node进行数据传输Directÿ…...
网络安全常见的35个安全框架及模型
大家读完觉得有帮助记得关注和点赞!!! 01、概述 网络安全专业机构制定的一套标准、准则和程序,旨在帮助组织了解和管理面临的网络安全风险。优秀的安全框架及模型应该为用户提供一种可靠方法,帮助其实现网络安全建设…...
Elasticsearch介绍及使用
Elasticsearch 是一款基于 Lucene 库构建的开源、分布式、RESTful 风格的搜索引擎和分析引擎,具有强大的全文搜索、数据分析、机器学习等功能,广泛应用于日志分析、实时数据分析、全文检索等场景。 核心概念 索引(Index)…...
Leetocde516. 最长回文子序列 动态规划
原题链接:Leetocde516. 最长回文子序列 class Solution { public:int longestPalindromeSubseq(string s) {int n s.size();vector<vector<int>> dp(n, vector<int>(n, 1));for (int i 0; i < n; i) {dp[i][i] 1;if (i 1 < n &&…...
iOS 逆向学习 - Inter-Process Communication:进程间通信
iOS 逆向学习 - Inter-Process Communication:进程间通信 一、进程间通信概要二、iOS 进程间通信机制详解1. URL Schemes2. Pasteboard3. App Groups 和 Shared Containers4. XPC Services 三、不同进程间通信机制的差异四、总结 一、进程间通信概要 进程间通信&am…...
高级生化大纲
一,蛋白质化学: 蛋白质分离是生物化学和分子生物学研究中的一项基本技术,用于根据蛋白质的物理和化学特性将其从混合物中分离出来。 1. 离心分离法 离心分离法利用离心力来分离不同质量或密度的颗粒和分子。 差速离心:通过逐…...
YARN WebUI 服务
一、WebUI 使用 与HDFS一样,YARN也提供了一个WebUI服务,可以使用YARN Web用户界面监视群集、队列、应用程序、服务、流活动和节点信息。还可以查看集群详细配置的信息,检查各种应用程序和服务的日志。 1.1 首页 浏览器输入http://node2.itc…...
【Unity3D】利用IJob、Burst优化处理切割物体
参考文章: 【Unity】切割网格 【Unity3D】ECS入门学习(一)导入及基础学习_unity ecs教程-CSDN博客 【Unity3D】ECS入门学习(十二)IJob、IJobFor、IJobParallelFor_unity ijobparallelfor-CSDN博客 工程资源地址&…...
【大前端】Vue3 工程化项目使用详解
目录 一、前言 二、前置准备 2.1 环境准备 2.1.1 create-vue功能 2.1.2 nodejs环境 2.1.3 配置nodejs的环境变量 2.1.4 更换安装包的源 三、工程化项目创建与启动过程 3.1 创建工程化项目 3.2 项目初始化 3.3 项目启动 3.4 核心文件说明 四、VUE两种不同的API风格 …...
基于文件系统分布式锁原理
分布式锁:在一个公共的存储服务上打上一个标记,如Redis的setnx命令,是先到先得方式获得锁,ZooKeeper有点像下面的demo,比较大小的方式判决谁获得锁。 package com.ldj.mybatisflex.demo;import java.util.*; import java.util.co…...
简历整理YH
一,订单中心 1,调拨单 融通(Rocketmq)-订单中心:ECC_BMS123(已出单),125(分配),127(发货),129(收货) 通过RocketMq接入多场景订单数据 2,销售单 sap(FTP)-订单中心,下发1002,1003,…...
用快马AI快速原型:30分钟搭建养龙虾智能养殖管理系统
用快马AI快速原型:30分钟搭建养龙虾智能养殖管理系统 养龙虾作为现代农业养殖项目,管理流程的数字化能显著提升养殖效率。最近我用InsCode(快马)平台快速搭建了一个智能养殖管理系统原型,整个过程比想象中简单很多。下面分享我的实现思路和关…...
Go Routine 调度策略详解
Go Routine 调度策略详解 Go语言凭借其轻量级的并发模型——Goroutine,成为高并发编程的热门选择。Goroutine的高效运行离不开Go调度器的智能管理,而调度策略则是其核心机制。本文将深入解析Goroutine的调度策略,帮助开发者更好地理解并发执…...
告别printf调试:手把手教你用STM32F411的USART6重定向标准输入输出
STM32F411串口调试革命:USART6重定向实战指南 在嵌入式开发中,调试信息的输出是开发者最依赖的工具之一。传统调试方式往往需要复杂的硬件调试器或频繁烧录程序,效率低下且不够灵活。本文将带你探索一种高效、便捷的调试方案——通过STM32F4…...
【QuantDev必藏】:为什么92%的C++交易系统仍在用malloc——深度剖析jemalloc/tcmalloc/mimalloc在L3缓存穿透场景下的失效临界点
第一章:金融高频交易系统内存分配的底层挑战与现实困境在纳秒级竞争的金融高频交易(HFT)场景中,内存分配不再是语言运行时的“黑盒服务”,而是决定订单延迟、吞吐一致性与系统可预测性的关键路径。传统堆分配器&#x…...
OpenClaw+Phi-3-vision智能相册:私人照片自动分类与摘要
OpenClawPhi-3-vision智能相册:私人照片自动分类与摘要 1. 为什么需要本地化的智能相册管理 去年夏天,我带着家人去海边度假,用手机拍了近千张照片。回来后面对杂乱的相册,花了整整两个周末才完成分类整理——这种痛苦经历让我开…...
Python绘图进阶:掌握颜色代码与实战应用
1. Python绘图中的颜色表示方法全解析 第一次用Python画图时,我对着那一堆颜色参数完全摸不着头脑。为什么同样的红色可以用"red"、"(1,0,0)"、"#FF0000"这么多种方式表示?后来才发现,这些不同的颜色表示方法各…...
千问3.5-27B中文优化实践:提升OpenClaw指令理解准确率
千问3.5-27B中文优化实践:提升OpenClaw指令理解准确率 1. 为什么需要专门优化中文指令理解 上周我在用OpenClaw整理项目文档时,发现一个有趣现象:当我用英文说"organize these PDFs by date"时,AI能准确按日期分类文件…...
TSMaster安全算法实战:如何用DLL快速实现SeedKey解锁(附常见错误排查)
TSMaster安全算法实战:如何用DLL快速实现Seed&Key解锁(附常见错误排查) 在汽车电子诊断领域,安全访问机制(Seed&Key)如同车辆的电子钥匙,是保护ECU数据安全的重要屏障。作为深耕诊断协议…...
终极指南:如何使用Ohm构建JavaScript解释器(10个完整步骤)
终极指南:如何使用Ohm构建JavaScript解释器(10个完整步骤) 【免费下载链接】ohm A library and language for building parsers, interpreters, compilers, etc. 项目地址: https://gitcode.com/gh_mirrors/oh/ohm Ohm是一个强大的解析…...
基于stm32的楼道照明系统[单片机]-计算机毕业设计源码+LW文档
摘要:本文提出了一种基于STM32单片机的楼道照明系统设计方案。该系统以STM32为核心控制器,结合人体热释电感应模块、声音感应模块和光照检测模块,实现楼道照明的智能控制。通过实时检测人体存在、声音信号以及环境光照强度,系统能…...
