Java多线程进阶:彻底搞懂线程池,拒绝盲目new Thread
前言在上一篇博客中我们梳理了Java线程的生命周期、线程创建的三种方式以及synchronized、volatile两大基础锁的底层原理。很多同学学完基础线程用法后写代码习惯性直接 new Thread().start() 开启新线程看似简单方便但在高并发场景下频繁创建和销毁线程会带来致命的性能问题1. 线程创建需要分配栈内存、内核资源开销极大2. 无限制创建线程会导致系统线程数量爆表引发OOM内存溢出3. 线程无法统一管理任务执行混乱无法监控线程状态。而线程池就是Java官方给出的最优解决方案提前创建好一批线程复用线程执行任务统一管理线程生命周期降低资源消耗、提升响应速度。今天一文吃透线程池核心参数、工作原理、四种常用线程池以及生产环境避坑指南。一、什么是线程池核心设计思想线程池本质是线程缓存池程序启动时预先初始化若干空闲线程当有异步任务需要执行时直接从池中取出空闲线程处理任务执行完毕线程不会被销毁而是归还线程池等待复用。核心优势总结✅ 降低开销避免频繁创建/销毁线程节省系统内核资源✅ 提高响应速度任务到来无需等待线程创建直接执行✅ 统一管控限制最大线程数防止并发过高压垮服务器✅ 可监控可扩展支持任务拒绝策略、线程超时回收、任务状态监控二、ThreadPoolExecutor七大核心参数重中之重实际开发中强烈不推荐使用Executors工具类创建线程池阿里开发手册明确强制要求手动通过ThreadPoolExecutor构造方法自定义线程池想要理解自定义线程池必须吃透7个核心参数。完整构造方法逐个参数详解1. corePoolSize核心线程数线程池中长期存活的常驻线程不会被超时回收。就算线程池空闲核心线程也会一直等待接收新任务相当于店里固定在岗的员工。2. maximumPoolSize最大线程数线程池能容纳的最大线程总数 最大线程数 核心线程数 非核心线程数 。非核心线程是临时扩容的线程空闲一段时间后会被回收。3. keepAliveTime空闲超时时间非核心线程的空闲等待时长当非核心线程空闲时间超过该值就会被自动销毁释放系统资源。4. unit时间单位空闲时间的单位时分秒毫秒常用 TimeUnit.SECONDS 。5. workQueue阻塞队列存储等待执行任务的队列当所有核心线程都在忙碌新任务会进入队列排队。常见队列- ArrayBlockingQueue有界阻塞队列必须指定容量- LinkedBlockingQueue无界阻塞队列不限制任务数量生产环境慎用6. threadFactory线程工厂用来创建线程一般自定义线程工厂给线程设置有意义的线程名线上排查bug日志时可以快速定位线程问题。7. handler拒绝策略当核心线程满队列满线程数达到最大值线程池彻底满载新任务触发拒绝策略JDK内置4种策略- AbortPolicy直接抛出异常默认策略- CallerRunsPolicy调用者线程自己执行任务不会丢弃任务- DiscardPolicy直接丢弃最新任务无异常- DiscardOldestPolicy丢弃队列最旧任务添加新任务三、线程池任务执行流程必考面试高频很多面试会问一个任务提交到线程池完整执行流程是什么 记住这套流程看懂所有线程池运行逻辑1. 提交任务首先判断核心线程数是否已满未满→创建核心线程执行任务已满→进入下一步2. 判断阻塞队列是否已满队列未满→任务存入队列排队队列已满→进入下一步3. 判断总线程数是否达到最大线程数未达到→创建非核心线程执行任务已达到→进入下一步4. 触发拒绝策略处理无法执行的新任务一句话总结先核心线程→再队列排队→再扩容非核心线程→最后拒绝四、为什么禁止使用Executors自带线程池初学阶段很多人会用 Executors.newFixedThreadPool() 等快捷方法创建线程池看似一行代码搞定实则暗藏大坑逐个拆解风险1. newFixedThreadPool固定线程池使用无界队列 LinkedBlockingQueue 任务堆积无上限高并发下任务越来越多直接OOM内存溢出。2. newCachedThreadPool缓存线程池最大线程数为Integer最大值无限制创建临时线程并发暴涨会直接耗尽服务器线程资源导致服务器卡死。3. newSingleThreadExecutor单线程池同样使用无界队列任务无限堆积引发OOM。结论Executors封装好的线程池屏蔽了底层参数屏蔽了风险生产环境必须手动创建ThreadPoolExecutor。五、手写自定义线程池实战代码结合业务场景编写一个适合接口异步处理的自定义线程池附带自定义线程工厂完整可直接运行代码参数选型思路业务通用- 核心线程数CPU密集型→CPU核心数1IO密集型→2*CPU核心数- 队列一律使用有界队列限制任务上限- 拒绝策略接口业务推荐CallerRunsPolicy保证任务不丢失六、日常开发线程池避坑总结1. 永远不要用Executors手动ThreadPoolExecutor创建线程池2. 必须使用有界阻塞队列杜绝任务无限堆积3. 必须自定义线程工厂方便线上日志排查问题4. 根据业务场景选择拒绝策略核心业务不能丢任务5. 定时任务优先使用ScheduledThreadPoolExecutor不混用普通线程池6. 服务关闭时务必调用shutdown()优雅关闭线程池避免资源泄漏下期预告下一篇博客我们讲解线程池源码深度解析execute方法底层源码逐行解读搞清楚线程池底层如何创建线程、如何入队、如何扩容彻底吃透面试源码考点同时补充CompletableFuture异步任务编排实战搞定Java高阶异步编程。写在最后线程池是Java后端开发的必会核心技能不管是日常业务异步处理、接口并发优化还是面试八股文都是重中之重。拒绝无脑new Thread规范使用线程池是从Java初学者进阶到合格后端开发的关键一步。有任何线程池参数、源码相关疑问欢迎评论区交流~