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

深入理解java线程池

一、概述
1.什么是线程池?
‌线程池(Thread Pool)‌ 是一种多线程处理机制,通过预先创建并管理一组线程来执行任务,处理完任务的线程不进行回收,而是阻塞等待下一个任务,避免频繁创建和销毁线程带来的性能开销。核心目标是 ‌复用线程资源‌、‌控制并发规模‌ 和 ‌提升系统稳定性‌。

2.为什么要使用线程池?
(1)‌降低资源消耗‌:避免频繁创建/销毁线程(线程创建成本高)。
(2)‌提高响应速度‌:任务到达时可直接使用已创建的线程来执行任务。
(3)统一管理任务‌:通过队列缓存任务,支持拒绝策略及监控。
(4)避免线程失控‌:限制线程数量,控制并发规模‌,防止因高并发导致系统崩溃。

二、如何创建线程池?
1.通过java中的Executors类来创建线程池。
Executors提供了6个静态方法,分别创建6种不同的线程池,六大静态方法 内部都是直接或间接调用ThreadPoolExecutor类的构造方法创建线程池对象,这六个静态方法本身是没有技术含量的。

下面介绍常用的四种:
(1)FixedThreadPool
  FixedThreadPool的特点:固定池子中线程的个数。使用静态方法newFixedThreadPool()创建线程池的时候指定线程池个数。

(2)CachedThreadPool(弹性缓存线程池)
  CachedThreadPool的特点:用newCachedThreadPool()方法创建该线程池对象, 创建之初里面一个线程都没有,当execute方法或submit方法向线程池提交任务时, 会自动新建线程;如果线程池中有空余线程,则不会新建;这种线程池一般最多情况可 以容纳几万个线程,里面的线程空余60s会被回收。

(3)SingleThreadPool(单线程线程池)
  SingleThreadPool的特点:池中只有一个线程,如果扔5个任务进来,那么有4个任务将排队;作用是保证任务的顺序执行。
(4)ScheduledThreadpool(定时器线程池)
 注意:要用ScheduledExecutorService去创建ScheduledThreadpool,如果用Executor去引用,就只能调用Executor接口中定义的方法;如果用ExecutorService接口去引用,就只能调用ExecutorService接口中定义的方法,无法使用ScheduledExecutorService接口中新增的方法,那么也就失去了这种线程池的意义。

2.使用ThreadPoolExecutor构建一个自定义线程池。

ExecutorService executorService = new ThreadPoolExecutor(5,10,10, TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(5),new ThreadPoolExecutor.CallerRunsPolicy());

3.使用Apache的guava中ThreadFactoryBuilder()来创建线程池。
(1)引入Guava依赖

<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>31.1-jre</version> <!-- 根据实际版本调整 -->
</dependency>

(2)使用ThreadFactoryBuilder创建线程池。
通过 ThreadFactoryBuilder 自定义线程工厂,并结合 ThreadPoolExecutor 创建线程池。

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.*;public class GuavaThreadPoolExample {public static void main(String[] args) {// 1. 创建线程工厂(设置线程名前缀、优先级等)ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("custom-pool-%d") // 线程名称格式(%d为线程编号).setDaemon(false)                // 非守护线程.setPriority(Thread.NORM_PRIORITY) // 默认优先级.setUncaughtExceptionHandler((t, e) -> System.err.println("线程" + t.getName() + "发生异常: " + e)).build();// 2. 创建线程池(手动指定参数)ExecutorService executor = new ThreadPoolExecutor(4,                              // 核心线程数(corePoolSize)8,                              // 最大线程数(maximumPoolSize)30, TimeUnit.SECONDS,           // 非核心线程空闲存活时间new LinkedBlockingQueue<>(100), // 有界队列(容量100)threadFactory,                  // 自定义线程工厂new ThreadPoolExecutor.AbortPolicy() // 拒绝策略);// 3. 提交任务executor.execute(() -> System.out.println("任务执行中"));// 4. 关闭线程池executor.shutdown();}
}

三、线程池核心组成
1、线程池的参数

(1) corePoolSize:核心线程数的大小

(2) maximumPoolSize:最大线程数的大小

(3) keepAliveTime:线程的空闲时间

(4) TimeUnit:空闲时间的单位

(5) workQueue:阻塞队列

(6) threadFactory:线程工厂

(7) Handler:拒绝策略
  
2.参数的详细说明:
  (1) corePoolSize:池子里的线程数的大小,设置allowCoreThreadTimeOut(true)使核心线程数内的线程也可以被回收。
  (2) maximumPoolSize:当池子里的线程数达到核心线程数的大小,队列也满了,可以继续创建的线程,直到线程数达到maximumPoolSize
  (3) keepAliveTime:线程的空闲时间,是跟核心线程数和最大线程数之间的线程相关,这部分线程,当到达规定的空闲时间还没有获取到任务,则会被回收
  (4) TimeUnit:空闲时间的单位
  (5) workQueue:默认支持4种阻塞队列
   ①ArrayBlockingQueue,基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。
   ②LinkedBlockingQuene,基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize      后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
  ③SynchronousQuene,一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,     如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
    ④PriorityBlockingQueue,具有优先级的无界阻塞队列,优先级通过参数Comparator实现。
  (6) threadFactory:线程工厂,用来创建一个新线程时使用的工厂,可以用来设定线程名,是否为daemon线程等
  (7) Handler:拒绝策略
    ①CallerRunsPolicy(直接拒绝任务),该策略下,在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务。
    ②AbortPolicy(直接丢弃任务,并抛异常),该策略下,直接丢弃任务,并抛出RejectedExecutionException异常。
    ③DiscardPolicy(直接丢弃任务,什么都不做),该策略下,直接丢弃任务,什么都不做。
    ④DiscardOldestPolicy(尝试添加新任务),该策略下,抛弃进入队列最早的那个任务,然后 尝试把这次拒绝的任务放入队列。

3.线程池运行过程
(1)开始运行时,线程池是空的
(2)当一个任务进来,则检查池中的线程数量,是否达到corePoolSize,如果没有达到,则创建线程,执行任务。
(3)任务执行完成之后,线程不会销毁,而是阻塞等待下一个任务
(4)此时又进来一个任务,但不是直接使用阻塞的线程,而是检查线程池中的线程数大小,是否达到corePoolSize,如果没有达到,则继续创建新的线程,来执行新的任务,如此往复,直到线程池中的线程数达到corePoolSize,此时停止创建新的线程。
(5)当又来新的任务,会选择线程池中阻塞等待的线程来执行任务,有一个任务进来,唤醒一个线程来执行这个任务,处理完之后,再次阻塞,尝试在workQueue上获取下一个任务,如果线程池中没有可唤醒的线程,则任务进入workQueue,排队等待。
(6)如果队列是无界队列,比如LinkedBlockingQueue,默认最大容量为Integer.MAX,接近于无界,可用无限制的接收任务,如果队列是有界队列,比如ArrayBlockingQueue,可限定队列大小,当线程池中的线程来不及处理,然后,所有的任务都进入队列,队列的任务数也达到限定大小,此时,再来新的任务,就会入队失败,然后,就会再次尝试在线程池里创建线程,直到线程数达到maximumPoolSize,停止创建线程。
(7)此时,队列满了,新的任务无法入队,创建的线程数也达到了maximumPoolSize,无法再创建新的线程,此时,就会reject掉,使用拒绝策略RejectedExecutionHandler,不让继续提交任务,默认的是AbortPolicy策略,拒绝,并抛出异常
(8) 超出corePoolSize数创建的那部分线程,是跟空闲时间keepAliveTime相关的,如果超过keepAliveTime时间还获取不到任务,线程会被销毁,自动释放掉。

4.如何关闭线程池?
接口ExecutorService的两种关闭线程池的方式,shutdown和shutdownNow方法:
(1)shutdown():拒收新的任务,立马关闭正在执行的任务,可能会引起报错,需要异常捕获
(2)shutdownNow():拒收新的任务,等待任务执行完毕,要确保任务里不会有永久等待阻塞的逻辑,否则会导致线程关闭不了。

四、线程池的应用场景
线程池的应用场景广泛,主要围绕‌资源复用‌、‌任务管理‌和‌性能优化‌展开。
1.使用 FixedThreadPool 或 CachedThreadPool 处理HTTP请求,避免主线程阻塞,提升吞吐量。
例如:在电商系统中,通过线程池快速处理用户下单请求。

2.网络I/O密集型任务,如文件上传、下载或API调用,利用线程池异步执行,减少I/O等待时间。

3.日志记录与监控
通过线程池异步写入日志或上报监控数据,避免同步操作影响主流程性能。

总结:
1.线程池的核心价值在于‌平衡资源利用与系统稳定性‌,在实际使用中,根据不同的需求选择线程池,如果需要单线程顺序执行,使用SingleThreadExecutor,如果已知并发压力,使用FixedThreadPool,固定线程数的大小,执行时间小的任务,可以使用CachedThreadPool,创建可缓存的线程池,可以无限扩大线程池,可以灵活回收空闲线程,最多可容纳几万个线程,线程空余60s会被回收,需要后台执行周期任务的,可以使用ScheduledThreadPool,可以延时启动和定时启动线程池。
2.实际应用中,应避免使用无界队列导致的内存溢出,最好使用ThreadPoolExecutor自定义线程池。

http://www.xdnf.cn/news/194923.html

相关文章:

  • stm32 阻塞式延时 与 非阻塞式延时
  • “数字驱动·智建未来——2025河北省建筑电气与智能化技术交流大会”
  • 【ACL系列论文写作指北14-科研心态与抗压管理】-走得远,比走得快更重要
  • 不同参数大小的DeepSeekR1模型对Java中new FileInputStream(“test.txt“).seek(100);语法错误的检查
  • 学习笔记:Qlib 量化投资平台框架 — MAIN COMPONENTS (Part I)
  • XrayR启动失败
  • 架构进阶:详解108页系统架构设计与详细设计知识讲座【附全文阅读】
  • 品融电商:全域电商代运营的领航者,驱动品牌长效增长
  • 第四章:Messaging and Memory
  • C语言中的指针详解
  • RSS‘25|CMU提出统一空中操作框架:以末端执行器为中心,无人机实现高精度遥操作
  • Cursor + Figma-Context-MCP ,让 Cursor 获取 Figma 设计图信息,实现 AI 生成页面的高度还原
  • 力扣面试150题--K 个一组翻转链表
  • 机器人--激光雷达
  • ESG跨境电商怎么样?esg跨境电商有哪些功用?
  • 阅读MySQL实战45讲第11天
  • uniapp打包apk如何实现版本更新
  • Spring MVC异常处理利器:深入理解HandlerExceptionResolver
  • SpringBoot实现接口防刷的5种高效方案详解
  • C#/.NET/.NET Core技术前沿周刊 | 第 36 期(2025年4.21-4.27)
  • AudioSet 音频中文类别
  • 蚂蚁seo蜘蛛池:提升网站收录的秘密武器
  • Nacos源码—1.Nacos服务注册发现分析二
  • 系统思考提升培训效能
  • 100天精通Python挑战总览 | 零基础到应用实战!
  • 安徽地区安全员A证考试中,哪些知识点是高频考点?
  • mysql8.0版本部署+日志清理+rsync备份策略
  • LLaMA-Factory部署以及大模型的训练(细节+新手向)
  • 基于 Java 的实现前端组装查询语句,后端直接执行查询方案,涵盖前端和后端的设计思路
  • Vue组件开发进阶:从通信原理到DOM异步更新实战