Java-JUC-Executors
Java JUC 工具包中 Executors 详解:线程池的 “开挂” 玩法!
兄弟们!今天咱来唠一唠 Java JUC 工具包里的Executors
!是不是在开发中经常遇到这种情况:多线程任务咔咔来,创建线程像不要钱一样,结果程序直接 “原地爆炸”?别慌!Executors
就是拯救你的 “多线程神器”!这篇文章咱就掰开揉碎了讲,保证让你彻底拿捏!
一、为啥要用 Executors?先看个 “血案现场”
上周新来的实习生写了个多线程任务处理的代码,好家伙,直接在循环里new Thread()
:
1 | for (int i = 0; i < 1000; i++) { |
结果服务器没扛住 10 分钟,CPU 直接飙到 100%,程序卡死!这就是典型的 “线程滥用”。创建太多线程不仅占用大量资源,还会导致频繁的上下文切换,效率反而更低。
而Executors
就像线程的 “智能管家”,它能帮我们:
复用线程:避免频繁创建和销毁线程,节省资源
控制并发:限制同时执行的线程数量,防止服务器被 “压垮”
统一管理:方便监控和管理线程任务,出了问题也好排查
二、Executors 的四大 “杀手锏”:创建线程池的核心方法
Executors
类提供了几个常用的静态方法来创建不同类型的线程池,每一个都有自己的 “绝活”!
1. newFixedThreadPool:固定大小的线程池
1 | ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5); |
核心参数:
5
表示线程池里固定有 5 个线程,不多也不少使用场景:适合任务量比较稳定,对资源占用有明确限制的场景。比如电商的订单处理,同时处理 5 个订单,不会因为订单突然增多而把服务器搞崩
底层原理:基于
ThreadPoolExecutor
实现,核心线程数和最大线程数都等于传入的参数(这里是 5),任务队列是无界队列LinkedBlockingQueue
,所以任务不会被拒绝,而是排队等待执行
2. newCachedThreadPool:可缓存的线程池
1 | ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); |
特点:线程数量不固定,可以无限扩大(理论上)。如果有空闲线程就复用,没有就创建新线程;当线程闲置 60 秒后会被回收
使用场景:适合处理突发性、短时间的大量任务。比如秒杀活动,瞬间大量请求过来,它能快速创建线程处理,活动结束后又自动回收线程
底层原理:核心线程数为 0,最大线程数为
Integer.MAX_VALUE
,任务队列是SynchronousQueue
,这个队列不存储任务,每个插入操作必须等待另一个线程的移除操作,所以任务一来就会创建新线程处理
3. newSingleThreadExecutor:单线程的线程池
1 | ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); |
作用:只有一个线程来执行所有任务,保证任务的顺序执行
使用场景:适合有顺序要求的任务,比如数据库的单表插入操作,保证数据不会因为多线程并发插入而出错
底层原理:其实就是固定大小为 1 的线程池,核心线程数和最大线程数都是 1 ,任务队列同样是
LinkedBlockingQueue
4. newScheduledThreadPool:定时任务线程池
1 | ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3); |
核心功能:可以定时执行任务,或者周期性地执行任务
使用场景:太实用了!比如电商的优惠券定时发放、系统的定时备份等。像这样延迟 3 秒执行任务:
1 | scheduledThreadPool.schedule(() -> { |
- 底层原理:基于
ScheduledThreadPoolExecutor
,核心线程数是传入的参数(这里是 3),最大线程数是Integer.MAX_VALUE
,任务队列是DelayedWorkQueue
,用于存储延迟执行的任务
5. newSingleThreadScheduledExecutor
前面四个方法已经很厉害了,但Executors
还有个 “隐藏大招”——newSingleThreadScheduledExecutor
!光听名字就能猜到,它结合了 “单线程” 和 “定时任务” 的双重特性,堪称 “时间管理大师”+“秩序守护者” 的结合体!
- 创建方式
1 | ScheduledExecutorService singleThreadScheduledExecutor = Executors.newSingleThreadScheduledExecutor(); |
就这么一行代码,一个单线程的定时任务线程池就创建好了!简单得让人怀疑它是不是真有两把刷子,别急,往下看!
- 功能特性
单线程执行:和
newSingleThreadExecutor
一样,它只有一个线程来执行任务。这就意味着所有任务都会老老实实排队,按照顺序一个一个执行,绝对不会出现任务执行顺序混乱的情况。就好比食堂打饭,大家都乖乖排好队,一个接一个打,秩序井然。定时 / 周期任务:又和
newScheduledThreadPool
类似,它能执行定时任务和周期性任务。比如延迟 5 秒执行某个任务,或者每隔 1 分钟执行一次任务,都能轻松搞定。
- 使用场景
顺序性要求高的定时任务:比如银行的账单生成任务,不仅要每天定时生成,还得保证每一步操作按顺序执行,不能乱了套。这时候
newSingleThreadScheduledExecutor
就派上用场了,先定时触发任务,再用单线程保证顺序,稳稳当当。资源敏感的定时任务:如果服务器资源紧张,不适合开多个线程执行定时任务,那这个单线程的定时任务线程池就能完美解决问题。它只用一个线程,不会占用过多资源,还能按时完成任务。
- 底层原理
它底层同样基于ScheduledThreadPoolExecutor
,不过核心线程数和最大线程数都设置为 1 ,任务队列依然是DelayedWorkQueue
。这样既保证了任务的定时 / 周期性执行,又通过单线程限制,确保任务执行的顺序性和资源可控性。
三、小心 “踩坑”!Executors 使用的注意事项
虽然Executors
很好用,但如果用错了,那就是 “埋雷”!
1. 慎用无界队列的线程池
newFixedThreadPool
、newSingleThreadExecutor
以及newSingleThreadScheduledExecutor
底层用的是无界队列LinkedBlockingQueue
,如果任务量突然暴增,队列会一直堆积任务,导致内存溢出!之前有个项目就因为这个问题,半夜服务器内存被占满,直接宕机!
2. 避免创建过多线程
newCachedThreadPool
虽然灵活,但要是任务量太大,创建的线程数可能会把服务器资源耗尽。建议在使用时结合业务场景,设置合理的线程上限
3. 及时关闭线程池
线程池使用完后,一定要记得关闭!不然会一直占用资源。可以用shutdown()
或shutdownNow()
方法:
1 | executorService.shutdown(); // 平滑关闭,等任务执行完再关闭 |
五、总结:Executors 的正确打开方式
固定任务量:用
newFixedThreadPool
,控制好线程数量,稳得一批突发任务:
newCachedThreadPool
能快速响应,但要注意线程上限顺序执行:
newSingleThreadExecutor
和newSingleThreadScheduledExecutor
保证任务按顺序执行,不出乱子定时任务:普通定时任务用
newScheduledThreadPool
,对顺序和资源要求高的定时任务就选newSingleThreadScheduledExecutor
兄弟们,看完这篇文章,Executors
的各种玩法就都被咱拿捏了!以后再遇到多线程任务,可别再 “瞎搞” 了,根据需求选对线程池,让代码性能直接 “起飞”!要是还有啥不明白的,评论区唠唠,咱一起把多线程这事儿彻底整明白!