静态代码块中使用 ExecutorService 执行多线程会出现什么情况呢?

AQS系列

1、AQS核心原理
2、ReentrantLock 原理及示例
3、CountDownLatch / Semaphore 示例及使用场景
4、BlockingQueue 示例及使用场景
5、静态代码块中使用 ExecutorService 执行多线程会出现什么情况呢?

文章目录

  • AQS系列
  • 一、 一般场景
  • 二、static {} 场景
  • 三、原理(why?)

一、 一般场景

我们在 MultiThreadExample.java 中 编写一个 exec 方法,这个方法中通过 Executors.newFixedThreadPool(3) 初始化一个线程池,线程数量是 3,随后则提交三个任务,最后用 executorService.shutdown() 关闭线程池,再用 awaitTermination 方法来等待所有的线程执行完,exec 方法运行结束了。
那一般我们是在 main 方法中直接调用 exec() 方法来执行,代码如下:

public class MultiThreadExample {public static void main(String[] args) {exec();}private static void exec(){System.out.println("任务开始执行...");// 创建一个固定大小的线程池,其中包含3个线程ExecutorService executorService = Executors.newFixedThreadPool(3);// 启动任务1executorService.execute(() -> {for (int i = 1; i <= 5; i++) {System.out.println("任务1 - 执行步骤 " + i);}});// 启动任务2executorService.execute(() -> {for (int i = 1; i <= 5; i++) {System.out.println("任务2 - 执行步骤 " + i);}});// 启动任务3executorService.execute(() -> {for (int i = 1; i <= 5; i++) {System.out.println("任务3 - 执行步骤 " + i);}});// 关闭线程池executorService.shutdown();try {// 等待所有任务执行完毕或者超时(这里设置超时为10秒)if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) {// 如果在超时时间内任务没有执行完毕,可以选择进行额外处理System.out.println("等待超时,可能有任务未完成");}} catch (InterruptedException e) {e.printStackTrace();}System.out.println("所有任务已经完成,程序退出。");}
}

运行结果:

任务开始执行...
任务1 - 执行步骤 1
任务1 - 执行步骤 2
任务1 - 执行步骤 3
任务1 - 执行步骤 4
任务1 - 执行步骤 5
任务2 - 执行步骤 1
任务2 - 执行步骤 2
任务2 - 执行步骤 3
任务2 - 执行步骤 4
任务2 - 执行步骤 5
任务3 - 执行步骤 1
任务3 - 执行步骤 2
任务3 - 执行步骤 3
任务3 - 执行步骤 4
任务3 - 执行步骤 5
所有任务已经完成,程序退出...

二、static {} 场景

其实和一般场景最大的区别就是,exec() 方法是在静态代码块中被调用的,也就是在 类加载 时就被执行的。

public class MultiThreadExample {static {exec();}public static void main(String[] args) {// 这里不管是否 new 效果都一样的new MultiThreadExample();}private static void exec(){System.out.println("任务开始执行...");// 创建一个固定大小的线程池,其中包含3个线程ExecutorService executorService = Executors.newFixedThreadPool(3);// 启动任务1executorService.execute(() -> {for (int i = 1; i <= 5; i++) {System.out.println("任务1 - 执行步骤 " + i);}});// 启动任务2executorService.execute(() -> {for (int i = 1; i <= 5; i++) {System.out.println("任务2 - 执行步骤 " + i);}});// 启动任务3executorService.execute(() -> {for (int i = 1; i <= 5; i++) {System.out.println("任务3 - 执行步骤 " + i);}});// 关闭线程池executorService.shutdown();try {// 等待所有任务执行完毕或者超时(这里设置超时为10秒)if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) {// 如果在超时时间内任务没有执行完毕,可以选择进行额外处理System.out.println("等待超时,可能有任务未完成");}} catch (InterruptedException e) {e.printStackTrace();}System.out.println("所有任务已经完成,程序退出。");}
}

运行结果:

任务开始执行...
等待超时,可能有任务未完成
所有任务已经完成,程序退出...
任务3 - 执行步骤 1
任务2 - 执行步骤 1
任务2 - 执行步骤 2
任务2 - 执行步骤 3
任务2 - 执行步骤 4
任务2 - 执行步骤 5
任务3 - 执行步骤 2
任务3 - 执行步骤 3
任务3 - 执行步骤 4
任务3 - 执行步骤 5
任务1 - 执行步骤 1
任务1 - 执行步骤 2
任务1 - 执行步骤 3
任务1 - 执行步骤 4
任务1 - 执行步骤 5

三、原理(why?)

我们可以看到 一般场景static {} 场景 的运行结果完全不同,static {} 场景 则出现了等待超时的情况,而不是等待所有线程都运行结束,这是为什么呢?

我们知道 static{} 是一个静态代码块,当 JVM 加载 MultiThreadExample 类时,会自动调用静态块中的 exec() 方法,这意味着线程池和线程会在 main() 方法之前就开始执行了,那为什么在 static{} 中调用 exec() 方法时会出现等待超时的情况呢?

出现 等待超时,可能是有任务未完成的情况是因为主线程在调用 executorService.shutdown() 后立即尝试通过 executorService.awaitTermination(10, TimeUnit.SECONDS) 来等待所有任务完成,但此时由于线程池中的线程尚未完全启动或正在执行中,所以主线程可能在给定的超时时间内无法确认所有任务是否已完成,从而导致超时。

那为什么在 main() 方法中调用 exec() 方法就不超时,所有线程都按预期执行完成,在 static{} 静态代码块中执行就会超时呢 ?
我们在 execute(() ->{})提交了任务之后,使用了 shutdown() 方法来关闭线程池,然后立即通过 awaitTermination(10, TimeUnit.SECONDS) 来等待所有线程都完成,但是此时线程池中的线程并没有开始执行或者正在执行中,而我们到 awaitTermination 方法中打断点会看到,程序执行到了 nanos = termination.awaitNanos(nanos); 这句,如图:
等待
为啥执行到了这句,因为上面的 runStateAtLeast(ctl.get(), TERMINATED) 判断返回了 false,runStateAtLeast 方法源码如下:

private static boolean runStateAtLeast(int c, int s) {
return c >= s;
}

说明线程池中的线程并没有执行完,c 是线程池中的目前的线程数量,s 是线程池中总的线程数量,看调用处传参就知道,传入的参数是 ctl.get()TERMINATED
termination.awaitNanos(nanos) 等待完之后,又赋值给 nanos,此时 nanos 的值是一个负数了,因为此方法中,经过了 LockSupport.parkNanos(this, nanosTimeout); 等待之后,返回是这样返回的 return deadline - System.nanoTime()

所以在 for 无线循环中,等待完 nanos 时间后,下次循环时 nanos 时小于 0 的,则返回了 false

那为什么线程池中的线程没有执行完呢?还需深究,此时先知道大概原因,后面待更新!


码友们中的大佬也可以解释下更底层原因,欢迎评论区留言讨论。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/669091.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AD高速板常见问题和过流自锁

可以使用电机减速器来增大电机的扭矩&#xff0c;低速运行的步进电机更要加上减速机 减速电机就是普通电机加上了减速箱&#xff0c;这样便降低了转速增大了扭矩 HDMI布线要求&#xff1a; 如要蛇形使其等长&#xff0c;不要在HDMI的一端绕线。 HDMI走线时两边拉线&#xff0…

忘记 RAG:拥抱Agent设计,让 ChatGPT 更智能更贴近实际

RAG&#xff08;检索增强生成&#xff09;设计模式通常用于开发特定数据领域的基于实际情况的ChatGPT。 然而&#xff0c;重点主要是改进检索工具的效率&#xff0c;如嵌入式搜索、混合搜索和微调嵌入&#xff0c;而不是智能搜索。 这篇文章介绍了一种新的方法&#xff0c;灵感…

信创ARM架构QT应用开发环境搭建

Linux ARM架构QT应用开发环境搭建 前言交叉工具链Ubuntu上安装 32 位 ARM 交叉工具链Ubuntu上安装 64 位 ARM 交叉工具链 交叉编译 QT 库下载 QT 源码交叉编译 QT 源码 Qt Creator交叉编译配置配置 Qt Creator Kits创建一个测试项目 小结 前言 有没有碰到过这种情况&#xff1…

layui

基于复杂结构的自定义模版相关介绍 我这里的接口给的格式数据 我这里搜索往返时候要显示成这样的 layui.use([table,form], function(){ var table layui.table; var form layui.form;// 渲染表格 table.render({ elem: #test-table-reload,toolbar: #toolbarDemo, …

【驱动】块设备驱动(四)-块设备驱动层

前言 块设备驱动程序是Liux块子系统中的最底层组件。它们从IO调度程序中获得请求&#xff0c;然后按要求处理这些请求。一个块设备驱动程序可能处理几个块设备。例如&#xff0c;IDE设备驱动程序可以处理几个IDE磁盘&#xff0c;其中的每个都是一个单独的块设备。而且&#xff…

vulnhub靶场之Thales

一.环境搭建 1.靶场描述 Description : Open your eyes and change your perspective includes 2 flags:user.txt and root.txt. Telegram: machineboy141 (for any hint) This works better with VIrtualBox rathe than VMware 2.靶场地址 https://www.vulnhub.com/entry/t…

ensp实验合集(二)

实验6 VLAN划分....................................................................... - 30 - 实验7 路由器调试及常用命令使用........................................ - 42 - 实验8 配置静态路由器............................................................…

GCC编译器的使用以及使用Makefile语法进行操控

Makefile 这里使用的Makefile操控编译器 gcc 常见的组成部分 c&#xff1a; gcc 的一个版本&#xff0c;默认语言设置为 C&#xff0c;而且在链接的时候自动包含标准 C 库。这和 g 一样configure&#xff1a; GCC 源代码树根目录中的一个脚本。用于设置配置值和创建 GCC 编…

pytest中fixture的使用方法

一、pytest中的fixture是什么 为可靠的和可重复执行的测试提供固定的基线&#xff08;可以理解为测试的固定配置&#xff0c;使不同范围的测试都能够获得统一的配置&#xff09;&#xff0c;fixture提供了区别于传统单元测试&#xff08;setup/teardown&#xff09;风格的令人…

2024最新版鸿蒙HarmonyOS开发工具安装使用指南

2024最新版鸿蒙HarmonyOS开发工具安装使用指南 By JacksonML 0. 什么是鸿蒙Harmony OS&#xff1f; 华为鸿蒙系统&#xff08;HUAWEI Harmony OS&#xff09;&#xff0c;是华为公司在2019年8月9日于东莞举行的华为开发者大会&#xff08;HDC.2019&#xff09;上正式发布的分…

蓝桥杯每日一题-----数位dp练习

题目 链接 参考代码 写了两个&#xff0c;一个是很久以前写的&#xff0c;一个是最近刚写的&#xff0c;很久以前写的时候还不会数位dp所以写了比较详细的注释&#xff0c;这两个代码主要是设置了不同的记忆数组&#xff0c;通过这两个代码可以理解记忆数组设置的灵活性。 im…

redis源码之:集群创建与节点通信(1)

一、创建集群与添加节点&#xff08;meet&#xff09; 通过redis源码之&#xff1a;redis-cli 集群命令发现&#xff0c;不管是新建cluster集群还是往集群里添加新节点&#xff0c;都是通过meet指令完成&#xff0c;假设有ABCD四个节点&#xff0c;新建集群&#xff1a;redis-…

Vue学习笔记之组件基础

1、组件的定义 一般将 Vue 组件定义在一个单独的 .vue 文件中&#xff0c;称做单文件组件&#xff1b;当然也可以将组件直接定义在js文件中&#xff0c;如下js代码&#xff0c;定义一个组件BlogPost&#xff0c;通过props定义对外暴露属性title&#xff0c;父组件传递title&am…

分享62个节日PPT,总有一款适合您

分享62个节日PPT&#xff0c;总有一款适合您 62个节日PPT下载链接&#xff1a;https://pan.baidu.com/s/1mheNtIvXknGHse44FW7nOw?pwd6666 提取码&#xff1a;6666 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 学习知识费力气&#xff0c;收集整理更不易…

2023年全球软件架构师峰会(ArchSummit上海站):核心内容与学习收获(附大会核心PPT下载)

微服务架构是当今软件架构的主流趋势之一。随着云计算和分布式系统的普及&#xff0c;越来越多的企业开始采用微服务架构来构建他们的应用。微服务架构可以将一个大型的应用拆分成多个小型的服务&#xff0c;每个服务都独立部署、独立运行&#xff0c;并通过轻量级的通信协议进…

非常好看的CSS加载中特效,引用css文件既可用

非常好看的CSS加载中特效 demo效果源码&#xff1a; <!DOCTYPE html5> <head><link rel"stylesheet" type"text/css" href"demo.css"/><link rel"stylesheet" type"text/css" href"loaders.css&…

【Qt解决】QIcon图标不显示以及LNK2019: 无法解析的外部符号问题

一句话解决 qmake重新构建&#xff0c;然后build&#xff0c;然后run 原因剖析 QIcon图标不显示 首先确保 qrc 文件已经添加对应图标文件&#xff0c;但是仍然不显示是因为没有编译新文件 LNK2019: 无法解析的外部符号 明明已经定义的槽函数&#xff0c;还是报这个错&…

开发板有线连主机,主机无线上网,开发板上网

网络配置&#xff1a; 以太网4连接开发板 wlan设置共享 vmwave需要禁用&#xff08;否则占用共享地址192.168.137.1&#xff09; 开发板 /etc/netplan 目录下&#xff1a;xx.yaml 00-installer-config.yaml /etc/resolv.conf route -n ifconfig 可以ping主机 可ping自己…

SpringBoot中的WebMvcConfigurer

SpringBoot中的WebMvcConfigurer 一、WebMvcConfigurer二、页面跳转控制器三、数据格式化1.Formatter\<T>2.内容转换器 四、拦截器 一、WebMvcConfigurer WebMvcConfigurer 作为配置类&#xff0c;采用 JavaBean 的形式来代替传统的 XML 配置文件形式&#xff0c;进而针…

SpringSecurity(18)——OAuth2授权码管理

AuthorizationCodeServices public interface AuthorizationCodeServices {//为指定的身份验证创建授权代码。String createAuthorizationCode(OAuth2Authentication authentication);//使用授权码。OAuth2Authentication consumeAuthorizationCode(String code)throws Invali…