《Java-SE-第二十六章》之线程池
前言
在你立足处深挖下去,就会有泉水涌出!别管蒙昧者们叫嚷:“下边永远是地狱!”
博客主页:KC老衲爱尼姑的博客主页
博主的github,平常所写代码皆在于此
共勉:talk is cheap, show me the code
作者是爪哇岛的新手,水平很有限,如果发现错误,一定要及时告知作者哦!感谢感谢!
文章目录
- 线程池概述
- 什么是线程池?
- 为什么从线程池拿会比直接创建线程快?
- Java标准库中的线程池
- 四种拒接策略演示
- Executors
- 简单实现线程池
- 线程池的基本逻辑
- 实现线程池的基本逻辑
线程池概述
什么是线程池?
线程虽然是轻量级进程,尽管线程比进程创建和销毁所消耗 的资源要少。但是如果线程的创建和销毁频率高了,开销也还是有的,为了进一步提高效率,引入了线程池,池子里面放着事先创建好的线程.后面用的时候直接从池子里面拿,如此速度就快了,但是代价线程池所需的空间,线程池就是以空间换时间。
为什么从线程池拿会比直接创建线程快?
因为创建线程和销毁线程是操作系统完成了,需要从用户态切换到内核态 这是耗时耗力 的。如果从线程池直接拿的话,就省去了切换到内核态的时间,同时当线程不用的时候直接放回到线程池即可。
Java标准库中的线程池
标准库中线程池为ThreadPoolExecutor类,该类中最主要是包含两类线程,一类是核心线程,另一类是非核心线程。当派发任务给线程池中的线程时,干活的是核心线程,当来的活太多了,核心线程不够用了,就会启动非核心线程。当活变少了,就会把非核心线程 给裁了。简单来说所谓的核心线程就像公司里面的正式工,非核心线程则是实习生。当公司人手不够的时候就会招多点实习生来干活,当活少了,实习生也就可以走了。
在Java8中,ThreadPoolExecutor一共提供了4个构造方法,在此主要介绍参数最多的,其他的三个构造方法都是这个构造方法减少参数而来的,所以搞懂了这个参数最多的构造方法,其他的自然而然也明白了。
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)
参数解释
- corePoolSize:表示核心线程数
- maximumPoolSize:池中允许的最大线程数,就是核心线程和非核心线程之和
- keepAliveTime:非核心线程在被终止之前等待新任务的最大时间,超过这个时间,该线程就会被停用。
- unit:时间单位
- workQueue:在任务执行之前用于保存任务的队列,该队列仅将保存submit方法提交的Runnable任务
- threadFactory:创建新线程 时所使用的工厂
- RejectedExecutionHandler:拒绝策略,执行被处理使用的处理程序,因为达到线程限制和对列容量
拒接策略详解
ThreadPoolExecutor中有四个静态内部类实现了RejectedExecutionHandler接口,分别对应四种不同的拒绝策略
- AbortPolicy:被拒绝的任务的处理程序,抛出一个
RejectedExecutionException。当活太多了,线程已经忙不过来了,还来活时,直接不处理,抛出异常。 - CallerRunsPolicy:任务从哪里来就回到哪里去。
- DiscardOldestPolicy:队列满了但是不会抛出异常,直接丢弃新任务,不做任何处理
- DiscardPolicy:队列满了, 丢弃工作队列中最旧的任务,然后尝试再次提交新任务,不会抛出异常。
常用方法
| 方法 | 解释 |
|---|---|
| submit() | 用于提交带有返回值的任务(Callable)和不带返回值的任务(Runnable)。 |
| execute() | 方法用于提交不带返回值的任务(Runnable)方法没有返回值,因此无法获取任务的执行结果或处理任务的异常。如果任务执行抛出异常,将会被线程池内部捕获并记录日志。 |
| shutdown() | 优雅地关闭线程池,避免资源泄漏和线程阻塞问题。停止接受新的任务,并等待已提交的任务执行完成 |
四种拒接策略演示
AbortPolicy
演示代码
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class ThreadPoolExecutorDemo {public static void main(String[] args) throws InterruptedException {ThreadPoolExecutor threadExecutor = new ThreadPoolExecutor(1,1,3,TimeUnit.SECONDS,new LinkedBlockingQueue<>(2),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());for (int i = 0; i < 5; i++) {final int taskId = i;threadExecutor.submit(()->someTask(taskId));}threadExecutor.shutdown();}/*** 定义一个需要并发执行的任务** @param taskId*/private static void someTask(int taskId) {System.out.println("Task " + taskId + " is starting...");try {Thread.sleep(100); // 模拟任务执行时间} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Task " + taskId + " is finished!");}
}
运行结果:

任务太多了,抛出异常之后就罢工了,不干活了。
CallerRunsPolicy
演示代码
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class ThreadPoolExecutorDemo {public static void main(String[] args) throws InterruptedException {ThreadPoolExecutor threadExecutor = new ThreadPoolExecutor(1,1,3,TimeUnit.SECONDS,new LinkedBlockingQueue<>(2),Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());for (int i = 0; i < 5; i++) {final int taskId = i;threadExecutor.submit(()->someTask(Thread.currentThread().getName(),taskId));}threadExecutor.shutdown();}/*** 定义一个需要并发执行的任务** @param taskId*/private static void someTask(String name,int taskId) {System.out.println(name+":Task " + taskId + " is starting...");try {Thread.sleep(100); // 模拟任务执行时间} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Task " + taskId + " is finished!");}
}
运行结果:

当任务过多时,直接拒接不干了,要干你自己干,所以有部分任务是main线程自己干的
DiscardOldestPolicy
代码演示
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class ThreadPoolExecutorDemo {public static void main(String[] args) throws InterruptedException {ThreadPoolExecutor threadExecutor = new ThreadPoolExecutor(1,1,3,TimeUnit.SECONDS,new LinkedBlockingQueue<>(1),Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardOldestPolicy());for (int i = 0; i < 5; i++) {final int taskId = i;threadExecutor.submit(()->someTask(Thread.currentThread().getName(),taskId));}threadExecutor.shutdown();}/*** 定义一个需要并发执行的任务** @param taskId*/private static void someTask(String name,int taskId) {System.out.println(name+":Task " + taskId + " is starting...");try {Thread.sleep(100); // 模拟任务执行时间} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Task " + taskId + " is finished!");}
}
运行结果:

DiscardPolicy
代码演示
import java.util.concurrent.*;public class ThreadPoolExecutorDemo {public static void main(String[] args) throws InterruptedException {ThreadPoolExecutor threadExecutor = new ThreadPoolExecutor(2,2,3,TimeUnit.SECONDS,new ArrayBlockingQueue<>(1),Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardPolicy());for (int i = 0; i < 10; i++) {final int taskId = i;threadExecutor.submit(()->someTask(Thread.currentThread().getName(),taskId));}threadExecutor.shutdown();}/*** 定义一个需要并发执行的任务** @param taskId*/private static void someTask(String name,int taskId) {System.out.println(name+":Task " + taskId + " is starting...");try {Thread.sleep(2000); // 模拟任务执行时间} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Task " + taskId + " is finished!");}
}
运行结果:

Executors
如果你觉得上述创建线程池的方式太复杂了,可以使用Executors来创建线程,其返回值是ExecutorService接口。Executors 本质上是 ThreadPoolExecutor 类的封装.
Executors 创建线程池的几种方式
-
newFixedThreadPool: 创建固定线程数的线程池
-
newCachedThreadPool: 创建线程数目动态增长的线程池.
-
newSingleThreadExecutor: 创建只包含单个线程的线程池.
-
newScheduledThreadPool: 设定 延迟时间后执行命令,或者定期执行命令. 是进阶版的 Timer.
使用演示
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ExecutorsDemo {public static void main(String[] args) {ExecutorService executorService = Executors.newCachedThreadPool();for (int i = 0; i < 5; i++) {final int taskId = i;executorService.submit(()->someTask(Thread.currentThread().getName(),taskId));}executorService.shutdown();}/*** 定义一个需要并发执行的任务** @param taskId*/private static void someTask(String name,int taskId) {System.out.println(name+":Task " + taskId + " is starting...");try {Thread.sleep(2000); // 模拟任务执行时间} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Task " + taskId + " is finished!");}
}
运行结果:

简单实现线程池
线程池的基本逻辑
线程池事先存放着准备好的线程,当有任务提交入池的时候,实际上是放入了阻塞队列中,然后线程池中的线程调度执行这些任务,在java中的线程池有核心线程和非核心线程,我们是简单实现,所以都是以核心线程的方式实现。
实现线程池的基本逻辑
使用阻塞队列组织所有的任务,定义一个线程池类其核心方法为submit()将任务添加到阻塞队列中,还需要一个工作线程不断向阻塞对列扫描获取任务并执行任务。
实现代码
MyThreadPool类实现
import java.util.concurrent.LinkedBlockingQueue;public class MyThreadPool {private int maxWorkerCount = 10;private LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue();public void submit(Runnable command) throws InterruptedException {if (queue.size() < maxWorkerCount) {// 当前 worker 数不足, 就继续创建 workerWorker worker = new Worker(queue);worker.start();}// 将任务添加到任务队列中queue.put(command);}}
Worker实现
import java.util.concurrent.LinkedBlockingQueue;public class Worker extends Thread {private LinkedBlockingQueue<Runnable> queue = null;public Worker(LinkedBlockingQueue<Runnable> queue) {super("worker");this.queue = queue;}@Overridepublic void run() {// try 必须放在 while 外头, 或者 while 里头应该影响不大try {while (!Thread.interrupted()) {Runnable runnable = queue.take();runnable.run();}} catch (InterruptedException e) {}}
}
测试代码
public class Demo {public static void main(String[] args) throws InterruptedException {MyThreadPool myThreadPool = new MyThreadPool();for (int i = 0; i < 5; i++) {final int taskId = i;myThreadPool.submit(() -> someTask(Thread.currentThread().getName(),taskId));}Thread.sleep(1000);}/*** 定义一个需要并发执行的任务** @param taskId*/private static void someTask(String name,int taskId) {System.out.println(name+":Task " + taskId + " is starting...");try {Thread.sleep(2000); // 模拟任务执行时间} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Task " + taskId + " is finished!");}}
运行结果:

各位看官如果觉得文章写得不错,点赞评论关注走一波!谢谢啦!。

相关文章:
《Java-SE-第二十六章》之线程池
前言 在你立足处深挖下去,就会有泉水涌出!别管蒙昧者们叫嚷:“下边永远是地狱!” 博客主页:KC老衲爱尼姑的博客主页 博主的github,平常所写代码皆在于此 共勉:talk is cheap, show me the code 作者是爪哇岛的新手,水平很有限&…...
【数据库】将excel数据导入mysql数据库
环境:Windows10 mysql8以上 将你要导入的excel表另存为txt格式 打开txt格式文件,删除表头行并另存为并更改编码方式(由于与数据库的编码不同,会导致导入报错) 通过命令行登录数据库 winr cmd进入 进入装mysql的目录位…...
无涯教程-Lua - repeat...until 语句函数
与 for 和 while 循环(它们在循环顶部测试循环条件)不同,Lua编程中的 repeat ... until 循环语言在循环的底部检查其条件。 repeat ... until 循环与while循环相似,不同之处在于,保证do ... while循环至少执行一次。 repeat...until loop - …...
环形链表 LeetCode热题100
题目 给你一个链表的头节点 head ,判断链表中是否有环。 思路 快慢指针。开始快指针在慢指针前面,当快指针等于慢指针时说明有环,如果快指针指向null时说明无环。 代码 /*** Definition for singly-linked list.* struct ListNode {* …...
使用python将每组两行数据合并一行
1、使用场景 将有规律的每组(一组2行)的单数行和双数行合并为一行,以空格分割。 比如使用pssh批量得出的结果,想让ip行和结果行合并为一行(前提如上所述) [rootk8s-master1 tmp]# pssh -h iplist -i hostname [1] 18:12:42 [SU…...
14-1_Qt 5.9 C++开发指南_网络编程及主机信息查询_HostInfo
Qt 网络模块提供了用于编写 TCP/IP 客户端和服务器端程序的各种类,如用于 TCP 通信的QTcpSocket 和 QTcpServer,用于 UDP 通信的 QUdpSocket,还有用于实现 HTTP、FTP 等普通网络协议的高级类如 QNetworkRequest,QNetworkReply 和Q…...
【iOS】通知原理
我们可以通过看通知的实现机制来了解通知中心是怎么实现对观察者的引用的。由于苹果对Foundation源码是不开源的,我们具体就参考一下GNUStep的源码实现。GNUStep的源码地址为:GNUStep源码GitHub下载地址, 具体源码可以进行查看。 通知的主要流程 通知全…...
创建邮件服务器(小微企业)
这里写自定义目录标题 目的硬件选型:软件选型:coremail (商业版本)postfixumail免费开源版本新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适…...
android app控制ros机器人四(调整界面布局)
半吊子改安卓,记录页面布局调整: 在ros-mobile基础上顶端增加一行,用于显示app名称和logo图像;修改标签页。 添加文字简单,但是替换图标长知识了,开始只是简单的把mipmap各个文件夹下的图片进行替换&…...
稍微深度踩坑haystack + whoosh + jieba
说到django的全文检索,网上基本推荐的都是 haystack whoosh jieba 的方案。 由于我的需求对搜索时间敏感度较低,但是要求不能有数据的错漏。 但是没有调试的情况下,搜索质量真的很差,搞得我都想直接用Like搜索数据库算了。 但是…...
微信小程序(van-tabs) 去除横向滚动条样式(附加源码解决方案+报错图)
问题描述 今天第一次接触vant组件库。 ant官网地址适用于Vue3 支持Vue2、Vue3、微信小程序等 我在使用van-tabs组件时遇到了一个问题,如下图所示: 从图片上可以看到有个灰色的横向滚动条,一开始领导给我说这个问题,我反反复复都…...
激光切割机所发出的辐射是否会对人体产生危害呢
激光切割设备所发出的激光作为一种特殊的能量光源,在一定程度上是存在辐射的。由于光纤激光器的功率通常大于半导体激光器,因此其辐射安全性也受到我们的关注。那么这种辐射的危害究竟有多大呢? 第一级:在正常操作下,不会发出对人…...
Redis 高可用:主从复制、哨兵模式、集群模式
文章目录 一、redis高可用性概述二、主从复制2.1 主从复制2.2 数据同步的方式2.2.1 全量数据同步2.2.2 增量数据同步 2.3 实现原理2.3.1 服务器 RUN ID2.3.2 复制偏移量 offset2.3.3 环形缓冲区 三、哨兵模式3.1 原理3.2 配置3.3 流程3.4 使用3.5 缺点 四、cluster集群4.1 原理…...
在GitHub上管理和协作的完全指南
介绍 GitHub 是一个强大的版本控制和协作平台,它不仅可以帮助你管理和跟踪项目的变化,还可以与他人进行协作。本文将详细介绍如何使用 GitHub 的各种功能来管理和协作项目。 目录 注册GitHub账号创建和管理仓库 创建仓库添加和管理文件分支管理合并请…...
git管理工具学习(图解使用git工作流程)
目录 GIT 简介一个最简单的GIT操作流程git的工作流程&命令 GIT 简介 git是什么,在维基百科上是这么介绍的:git是一个分布式的版本控制软件 分布式是相对于集中式而言的,分布式即每一个git库都是一个完整的库。 每个库的地位都是平等的&am…...
单例模式(Singleton)
单例模式保证一个类仅有一个实例,并提供一个全局访问点来访问它,这个类称为单例类。可见,在实现单例模式时,除了保证一个类只能创建一个实例外,还需提供一个全局访问点。 Singleton is a creational design pattern t…...
2023-08-02 LeetCode每日一题(翻转卡片游戏)
2023-08-02每日一题 一、题目编号 822. 翻转卡片游戏二、题目链接 点击跳转到题目位置 三、题目描述 在桌子上有 N 张卡片,每张卡片的正面和背面都写着一个正数(正面与背面上的数有可能不一样)。 我们可以先翻转任意张卡片,…...
JAVAWEB项目--POST完整交互(servlet,axios,JavaScript)
post交互 js: axios.post("/mycsdn/blog/pageSer", {currentPage:currentPage,}).then(function (response) {window.location.href url;}).catch(function (error) {console.error("分页未遂", error);}); 后端servlet: public…...
统一观测|借助 Prometheus 监控 ClickHouse 数据库
引言 ClickHouse 作为用于联机分析(OLAP)的列式数据库管理系统(DBMS), 最核心的特点是极致压缩率和极速查询性能。同时,ClickHouse 支持 SQL 查询,在基于大宽表的聚合分析查询场景下展现出优异的性能。因此,获得了广泛的应用。本文旨在分享阿…...
【Golang】基于录制,自动生成go test接口自动化用例
目录 背景 框架 ginkgo初始化 抓包&运行脚本 目录说明 ∮./business ∮./conf ∮./utils ∮./testcase testcase 用例目录结构规则 示例 实现思路 解析Har数据 定义结构体 解析到json 转换请求数据 转换请求 转换请求参数 写业务请求数据 写gotest测试…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...
MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...
