《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测试…...
51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...
基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...
【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
