Java线程池-执行顺序

文章目录

参考文档: 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

五、扩展

虚拟线程池

很新版本特性,了解即可。

相关推荐
KIKIiiiiiiii几秒前
微信个人号API二次开发中的解决经验
java·人工智能·python·微信
80530单词突击赢1 分钟前
SpringBoot整合SpringMVC全解析
java·spring boot·后端
vx1_Biye_Design11 分钟前
基于Spring Boot+Vue的学生管理系统设计与实现-计算机毕业设计源码46223
java·vue.js·spring boot·spring·eclipse·tomcat·maven
vx_Biye_Design13 分钟前
基于Spring Boot+vue的湖北旅游景点门票预约平台的设计--毕设附源码29593
java·vue.js·spring boot·spring cloud·servlet·eclipse·课程设计
hay_lee31 分钟前
Spring AI实现对话聊天-流式输出
java·人工智能·ollama·spring ai
Hx_Ma1637 分钟前
SpringBoot数据源自动管理
java·spring boot·spring
SunnyDays101138 分钟前
Java 高效实现 CSV 转 Excel
java·csv转excel
starfire_hit39 分钟前
JAVAWEB根据前台请求获取用户IP
java·服务器·网络
fengxin_rou40 分钟前
[Redis从零到精通|第四篇]:缓存穿透、雪崩、击穿
java·redis·缓存·mybatis·idea·多线程
像少年啦飞驰点、44 分钟前
从零开始学 RabbitMQ:小白也能懂的消息队列实战指南
java·spring boot·微服务·消息队列·rabbitmq·异步编程