文章目录
参考文档: https://javaj.blog.csdn.net/article/details/155262391
这篇博客讲解的很清楚了,我这里主要是把里面内容实践了下,在结合源码总结下。
一、代码地址
https://gitee.com/YaChiXiaoLiao/nkw/blob/master/src/threadPool/ExecutorsDemo.java
二、线程池核心参数
7个核心参数。 我们可以通过这个Demo点进去详细查看。
java
// 1. 固定线程池 核心线程数=最大线程数,无空闲线程超时
//场景: 任务量稳定,避免线程频繁创建销毁
ExecutorService fixedPool = Executors.newFixedThreadPool(3); //3个固定工人


拒绝策略有四种,分别是ThreadPoolExecutor类的四个静态内部类:


三、线程池执行顺序
那么线程池到底怎么执行呢?这就跟上面的7个参数密切相关了。先说结论,然后我们根据代码示例运行结果来验证。
假设这7个参数设置的值对应如下:
java
int corePoolSize, 3
int maximumPoolSize, 5
long keepAliveTime, 1
TimeUnit unit, 秒
BlockingQueue<Runnable> workQueue, new ArrayBlockingQueue<>(10)
ThreadFactory threadFactory, 一个工厂
RejectedExecutionHandler handle new ThreadPoolExecutor.CallerRunsPolicy()
假设来了16个任务,那么任务1、2、3会直接分配给3个corePoolSize去执行;任务4~13这10个任务会直接进入队列------ArrayBlockingQueue里,因为队列可以容纳10个任务(此时3个corePoolSize的任务都在进行中,如果期间有完成的,会从队列里拿而非直接抢断新的任务(新任务还是会进队列等待)),这里先简化为都在执行。然后队列已满,来的第14、15个任务如何处理?此时会比较corePoolSize和maximumPoolSize,发现corePoolSize=3 < maximumPoolSize=5, 那么好有2个空闲,此时会再创建2个临时线程,所以任务14、任务15交给了刚新创建的2个临时线程处理;好了,第16个任务来了,发现前面15个线程都在处理中,且队列已经放不下了,已经超过了处理能力上限,就按照CallerRunsPolicy策略来处理,这个策略含义是交给调用方的线程去处理,所以第16个任务会是主线程处理,而非线程池里的非守护线程处理。
看下AI的总结:
java
线程池的任务分配 + 空闲线程处理逻辑是 "两步走",这是你场景的核心依据:
步骤 1:新任务提交时的分配逻辑(优先级)
当提交一个新任务时,线程池会按以下顺序判断:
核心线程池是否有空闲? → 有则核心线程执行新任务;
核心线程池已满? → 检查任务队列是否未满 → 未满则新任务入队列;
队列也满了? → 检查是否达到最大线程数 → 未到则创建非核心线程执行新任务;
已到最大线程数? → 执行拒绝策略。
步骤 2:核心线程空闲后的任务获取逻辑
核心线程执行完当前任务后,不会 "主动抢新提交的任务",而是优先从任务队列中获取等待的任务执行(这是线程池 "复用核心线程、减少线程创建开销" 的设计核心)。
四、代码示例
1、示例1-ExecutorsDemo
运行结果:
java
主线程:创建第1 个任务
线程池中的异步任务执行【开始】
主线程循环到:1
主线程:创建第2 个任务
主线程循环到:2
主线程:创建第3 个任务
线程池中的异步任务执行【开始】
主线程循环到:3
主线程:创建第4 个任务
线程池中的异步任务执行【开始】
主线程循环到:4
主线程:创建第5 个任务
主线程循环到:5
线程 pool-1-thread-1 处理任务1
线程 pool-1-thread-3 处理任务3
线程 pool-1-thread-2 处理任务2
线程池中的异步任务执行【完毕】
线程池中的异步任务执行【完毕】
线程池中的异步任务执行【开始】
线程 pool-1-thread-3 处理任务5
线程池中的异步任务执行【完毕】
线程池中的异步任务执行【开始】
线程 pool-1-thread-2 处理任务4
线程池中的异步任务执行【完毕】
线程池中的异步任务执行【完毕】
1、注意主线程和非守护线程执行顺序;
观察发现execute里的日志:"线程池中的异步任务执行【开始】"的顺序很奇怪。按理说应该和非守护线程一起异步执行,但实际结果却基本和主线程一同打印了出来。
2、3个核心线程执行之后,其他2个任务应该是进入队列里。2、3执行完毕后继续从队列里取,执行4、5。4、5在队列里虽然有顺序,但是线程竞争拿的时候就未必会按照队列顺序拿,所以这里出现了先处理任务5,后处理任务4的情况。
所以多执行几次,每次效果不一样的:
java
主线程:创建第1 个任务
线程池中的异步任务执行【开始】
主线程循环到:1
主线程:创建第2 个任务
主线程循环到:2
主线程:创建第3 个任务
线程池中的异步任务执行【开始】
主线程循环到:3
主线程:创建第4 个任务
线程池中的异步任务执行【开始】
主线程循环到:4
主线程:创建第5 个任务
主线程循环到:5
线程 pool-1-thread-2 处理任务2
线程 pool-1-thread-1 处理任务1
线程 pool-1-thread-3 处理任务3
线程池中的异步任务执行【完毕】
线程池中的异步任务执行【完毕】
线程池中的异步任务执行【开始】
线程池中的异步任务执行【完毕】
线程 pool-1-thread-1 处理任务4
线程池中的异步任务执行【开始】
线程 pool-1-thread-2 处理任务5
线程池中的异步任务执行【完毕】
线程池中的异步任务执行【完毕】
2、示例2-ThreadPoolExecutorDemo
执行结果:
java
线程-【业务线程池-线程-3】处理任务3
线程-【业务线程池-线程-4】处理任务14
线程-【业务线程池-线程-1】处理任务1
线程-【业务线程池-线程-2】处理任务2
线程-【业务线程池-线程-5】处理任务15
线程-【main】处理任务16
JVM退出,线程池优雅关闭
线程-【业务线程池-线程-5】处理任务6
线程-【业务线程池-线程-1】处理任务4
线程-【业务线程池-线程-4】处理任务7
线程-【业务线程池-线程-2】处理任务8
线程-【业务线程池-线程-3】处理任务5
线程-【业务线程池-线程-1】处理任务9
线程-【业务线程池-线程-5】处理任务10
线程-【业务线程池-线程-2】处理任务11
线程-【业务线程池-线程-3】处理任务12
线程-【业务线程池-线程-4】处理任务13
1)这个可以充分证明线程池的执行顺序,任务14、15是在队列满了后,发现核心线程3<最大线程5,所以创建了2个非核心线程来处理。
2)jvm虽然退出,但是非守护线程还可以继续执行;
3)主线程处理了任务16,是按照new ThreadPoolExecutor.CallerRunsPolicy() //拒绝策略 :提交者自己执行(这里就是main方法)来执行的。且顺序会变化。
多次执行发现:
java
线程-【main】处理任务16
线程-【业务线程池-线程-3】处理任务3
线程-【业务线程池-线程-1】处理任务1
线程-【业务线程池-线程-4】处理任务14
线程-【业务线程池-线程-5】处理任务15
线程-【业务线程池-线程-2】处理任务2
JVM退出,线程池优雅关闭
线程-【业务线程池-线程-1】处理任务5
线程-【业务线程池-线程-3】处理任务4
线程-【业务线程池-线程-4】处理任务6
线程-【业务线程池-线程-5】处理任务7
线程-【业务线程池-线程-2】处理任务8
线程-【业务线程池-线程-1】处理任务9
线程-【业务线程池-线程-5】处理任务11
线程-【业务线程池-线程-4】处理任务10
线程-【业务线程池-线程-3】处理任务12
线程-【业务线程池-线程-2】处理任务13
五、扩展
虚拟线程池
很新版本特性,了解即可。
