springboot优雅shutdown时如何保障异步线程的安全
我前面写了一篇springboot优雅shutdown的文章,看起来一切很美好。
https://blog.csdn.net/chenshm/article/details/139640775
那是因为没有进行多线程测试。如果一个请求中包括阻塞线程(主线程)和非阻塞线程(异步线程),会是什么效果?接下来我们就测试一番。
1. 验证优雅shutdown的异步线程安全性
- 确认graceful shutdown配置

查看源码可以看到springboot graceful shutdown默认只会等待30s,我这里设置更长的时间只是方便测试,实际设置还是需要根据你业务api最长执行时间来配置。

- 准备测试代码
@Slf4j
@RestController
@RequestMapping("/api")
public class DemoController {@GetMapping("/{userId}")public ResultVo<Object> getUserInfo(@PathVariable String userId) throws InterruptedException {log.info("userId:{}", userId);Runnable runnable = () -> {for (int i = 0; i < 60; i++) {log.info("async thread to update user login info to other services, service num: {}", i);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}};Thread thread = new Thread(runnable);thread.start();for (int i = 0; i < 30; i++) {log.info("querying user info for {}, waiting times: {}", userId, i);Thread.sleep(1000);}return ResultVo.ok();}
}
这里我设置非阻塞线程的循环是60次,大概60s完成,阻塞线程循环只有30次,大概30s完成。主要是为了测试我的阻塞线程完成后,graceful shutdown能不能保证我的异步线程安全。
- 请求api
Administrator@USER-20230930SH MINGW64 /d/git/micro-service-logs-tracing
$ curl http://localhost:8080/api/sandwich
- shutdown app(Ctrl+F2)
- 查看日志

可以看到shutdown信号发出之后,两个线程都还在跑,但是阻塞线程(0-29)结束之后,异步线程也跟着终结了。它的循环应该是从0到59才算结束,但是只跑到30,所以异步线程是不安全的。
- 验证主线程返回结果
阻塞线程还是安全的,response正常返回了。

其实这种测试方法并不局限于解决springboot的问题,其他微服务也是类似的。过去我看到一些朋友测试release的安全性,只是不断call health api,只要release 期间health api没有返回异常就当作ok了,其实这只能验证你的负载均衡服务的可靠性,你自己app的安全问题还是没有得到解决。
既然问题找到了,接下来我来解决它。
2. 确保优雅shutdown app时异步线程也安全
2.1 优化代码
前面的异步线程只是简单地写个野线程,并不规范,我先优化一下。
- 把野线程放到线程池执行;
- 利用mbean的PreDestroy来在servcie销毁前先等待异步线程完成;
- 利用ExecutorService 的awaitTermination方法预判断异步线程的最长等待时间,等待异步线程完成,如果线程没有按时完成再强制结束。
@Slf4j
@Service
public class AsyncServiceImpl implements AsyncService {private final ExecutorService executorService;public AsyncServiceImpl() {this.executorService = Executors.newFixedThreadPool(10);}@Overridepublic void feedUserInfoToOtherServices(String userId) {executorService.execute(() -> {for (int i = 0; i < 35; i++) {log.info("async thread to update {} login info to other services, service num: {}", userId, i+1);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});}@PreDestroypublic void tearDown() {if (null != executorService) {executorService.shutdown();try {if (!executorService.awaitTermination(50, TimeUnit.SECONDS)) {executorService.shutdownNow();}} catch (InterruptedException e) {log.info("PreDestroy executorService is interrupted", e);executorService.shutdownNow();}}}
}
api代码调整如下
@Slf4j
@RestController
@RequestMapping("/api")
public class DemoController {@ResourceAsyncService asyncService;@GetMapping("/{userId}")public ResultVo<Object> getUserInfo(@PathVariable String userId) throws InterruptedException {log.info("userId:{}", userId);asyncService.feedUserInfoToOtherServices(userId);for (int i = 0; i < 30; i++) {log.info("updating user info for {}, waiting times: {}", userId, i+1);Thread.sleep(1000);}return ResultVo.ok();}
}
2.2 验证shutdown过程异步线程的安全
从新代码看来,我们期待的结果是一个api请求,主线程循环从1到30,异步线程是从1到35,主线程先完成,异步线程会在AsyncServiceImpl servcie bean销毁前先等待异步线程完成。接下来是验证步骤。
- 重启服务
- call api
Administrator@USER-20230930SH MINGW64 /d/git/micro-service-logs-tracing
$ curl http://localhost:8080/api/sandwich
- shutdown app(Ctrl + F2)
- 查看日志

分析日志发现一切如代码所料,app graceful shutdown的时候,异步线程的安全性得到保障。
这个过程看起来非常完美,其实还不够完美,解决方案没最好,只有更好。请先关注我,容我研究一下,下期告诉你为什么。
相关文章:
springboot优雅shutdown时如何保障异步线程的安全
我前面写了一篇springboot优雅shutdown的文章,看起来一切很美好。 https://blog.csdn.net/chenshm/article/details/139640775 那是因为没有进行多线程测试。如果一个请求中包括阻塞线程(主线程)和非阻塞线程(异步线程)…...
C++格式化库fmt使用方法
1. 格式化库fmt简介 fmt github地址 api说明 格式化参数说明 内容的格式化,体现在代码中主要表现为字符串、基本类型、自定义类型的拼接。例如说打印日志、拼接变量等。C中我们会经常使用类似printf,snprintf(C风格使用不方便),std::string.append(繁琐), std::io…...
HTML 颜色名:网页设计的调色板
HTML 颜色名:网页设计的调色板 在网页设计和开发中,颜色是一个关键元素,它不仅影响视觉效果,还能传达情感和品牌信息。HTML 颜色名是用于在 HTML 和 CSS 代码中指定颜色的预定义名称。这些颜色名易于记忆,方便设计师和开发者快速选择和应用颜色。本文将详细介绍 HTML 颜色…...
12306 火车票价格解析 (PHP 解析)
1. 从接口拿数据 日期 出发站 终点站 都填上 xxx/otn/leftTicketPrice/queryAllPublicPrice?leftTicketDTO.train_date2024-06-15&leftTicketDTO.from_stationBJP&leftTicketDTO.to_stationSJP&purpose_codesADULT 返回的数据是这样的 {"validateMess…...
了解统计学中不同类型的分布
目录 一、说明 二、均匀分布: 三、机器学习和数据科学中的均匀分布示例: 3.1 对数正态分布: 3.2 机器学习和数据科学中的对数正态分布示例: 四、 帕累托分布 4.1 什么是幂律? 4.2 机器学习和数据科学中的帕累托分布示例…...
k8s-CCE创建工作负载变量引用
CCE创建工作负载变量引用 背景,看到cce创建负载时会生成变量,如下。在skywaking-agent的使用,想要调用cce负载变量生成service_name。 -Dskywalking.agent.authentication里含有敏感信息需要写到配置项。简单粗糙的都写到配置项好像不合适。…...
后端主流框架--Spring02
前言:上篇关于Spring的文章介绍了一些Spring的基本知识,此篇文章主要分享一下如何配置Spring环境,如何注入等。 Spring项目构建 导入Spring相关JAR包 <dependency><groupId>org.springframework</groupId><artifactId>spring…...
[数据集][目标检测]减速带检测数据集VOC+YOLO格式5400张1类别
数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):5400 标注数量(xml文件个数):5400 标注数量(txt文件个数):5400 标注…...
分析Linux操作指令及使用场景与频率分析 持续更新
本篇主要针对在日常工作与学习中使用较多的linux指令的使用方法以及使用频次进行分析与讲解,旨在能够更好的掌握这些必备的技能。 linux指令非常的多,如果要记住所有的指令使用方法是非常困难的且要花费很长的时间,很多人习惯离开使用去通篇…...
Redis 字符串(String)
Redis 字符串(String) 介绍 Redis是一种开源的、高性能的键值数据库,它支持多种类型的数据结构,其中字符串(String)是Redis中最基本的数据类型之一。字符串类型可以存储任何形式的字符串,包括文本、序列化的对象或二进制数据。在Redis中,字符串类型的最大容量为512MB。 …...
第一篇:容器化的未来:从Docker的革命到云原生架构
容器化的未来:从Docker的革命到云原生架构 1. 引言 在当今快速演进的技术领域,容器化技术已经成为云计算和微服务架构的重要组成部分。该技术以其高效的资源利用率、快速的部署能力和卓越的隔离性能,彻底改变了软件开发和部署的方式。容器化…...
【2024最新华为OD-C/D卷试题汇总】[支持在线评测] URL拼接(100分) - 三语言AC题解(Python/Java/Cpp)
🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 📎在线评测链接 URL拼接(100分) 🌍 评测功能需要订阅专栏后私信联系清隆解…...
反射,枚举以及lambda表达式
【本节目标】 1. 掌握反射 2. 掌握枚举 3. 掌握lambda表达式使用 反射 1 定义 Java的反射(reflection)机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调…...
DNS域名解析----分离解析、多域名解析、父域与子域
1 理论部分 1.1 分离解析 DNS的分离解析,是指根据不同的客户端提供不同的域名解析记录。来自不同地址的客户机请求解析同一域名时,为其提供不同的解析结果。也就是内外网客户请求访问相同的域名时,能解析出不同的IP地址,实现负载…...
Spring底层架构核心概念解析
BeanDefinition BeanDefinition表示Bean定义,BeanDefinition中存在很多属性用来描述一个Bean的特点.比如: beanClass:表示Bean类型scope:表示Bean作用域,单例/原型等lazyInit:表示Bean是否懒加载initMethodName:表示Bean初始化时要执行的方法destoryMethodName:表示Bean销毁时…...
C++ 44 之 指针运算符的重载
#include <iostream> #include <string> using namespace std;class Students04{ public:int m_age;Students04(int age){this->m_age age;}void showAge(){cout << "年龄是: " << this->m_age << endl;}~Students0…...
onlyoffice在线预览加载优化
背景: 使用容器部署onlyoffice到linux服务器,使用内网访问速度还可以接受,但是如果放到外网路径访问起来,速度就会很慢,甚至加载失败; 优化方案: 预览的过程排除网络因素,可以发现打…...
依赖自动装配
黑马程序员SSM框架 文章目录 1、依赖自动装配2、依赖自动装配的特征 1、依赖自动装配 IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配自动装配方式 按类型(常用)按名称按构造方法不启用自动装配 配置中使用bean标签auto…...
mysql和redis的双写一致性问题
一,使用方案 在使用redis作为缓存的场景下,我们一般使用流程如下 二,更新数据场景 我们此时修改个某条数据,如何保证mysql数据库和redis缓存中的数据一致呢? 按照常规思路有四种办法,1.先更新mysql数据&a…...
Qwen2——阿里巴巴最新的多语言模型挑战 Llama 3 等 SOTA
引言 经过几个月的期待, 阿里巴巴 Qwen 团队终于发布了 Qwen2 – 他们强大的语言模型系列的下一代发展。 Qwen2 代表了一次重大飞跃,拥有尖端的进步,有可能将其定位为 Meta 著名的最佳替代品 骆驼3 模型。在本次技术深入探讨中,我…...
多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...
基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
