当前位置: 首页 > news >正文

Spring Boot 3虚拟线程的使用

在Spring Boot非Web应用中,使用虚拟线程时程序提前终止的问题及解决方案,可以通过以下步骤深入理解和验证:

问题根源分析

  1. JVM退出机制
    Java中,当所有非守护线程结束时,JVM会立即退出。即使存在正在运行的守护线程(如虚拟线程),JVM也不会等待它们完成。

  2. 虚拟线程的特性

    • 虚拟线程(Thread.ofVirtual()默认是守护线程,且无法通过setDaemon(false)修改。
    • 虚拟线程由JVM管理,不直接映射到操作系统线程,因此其生命周期与JVM的退出策略紧密相关。
  3. 非Web应用的默认行为
    非Web应用启动后,若没有其他非守护线程(如主线程、定时任务线程),JVM会立即退出。即使启用了虚拟线程执行任务,由于虚拟线程是守护线程,无法阻止JVM退出。

解决方案验证

Spring Boot从3.2.0-RC1开始提供spring.main.keep-alive=true配置,其原理如下:

  1. KeepAlive监听器
    启用后,Spring Boot会注册一个KeepAlive监听器,在上下文刷新完成后启动一个非守护线程(命名为keep-alive),该线程无限期休眠(Thread.sleep(Long.MAX_VALUE)),确保JVM不会退出。

  2. 线程终止逻辑
    当Spring上下文关闭时(如调用SpringApplication.exit()),KeepAlive监听器会中断keep-alive线程,允许JVM正常退出。

验证实验

实验1:未启用keep-alive
// 虚拟线程执行任务
OfVirtual virtual = Thread.ofVirtual().name("Task-");
virtual.start(() -> {
    System.out.println("任务开始");
    try { TimeUnit.SECONDS.sleep(5); } 
    catch (InterruptedException e) {}
    System.out.println("任务结束");
});
// 主线程休眠1秒
TimeUnit.SECONDS.sleep(1);

结果:仅输出任务开始,程序立即退出。
原因:虚拟线程是守护线程,主线程结束后JVM直接退出。

实验2:启用keep-alive

application.properties中添加:

spring.main.keep-alive=true

结果:程序持续运行5秒,完整输出任务开始任务结束
原因keep-alive线程阻止JVM退出,等待虚拟线程任务完成。

扩展建议

  1. 任务完成检测
    如果任务需要显式通知完成,可结合CountDownLatchCompletableFuture

    CountDownLatch latch = new CountDownLatch(1);
    OfVirtual virtual = Thread.ofVirtual().name("Task-");
    virtual.start(() -> {
        try { /* 执行任务 */ }
        finally { latch.countDown(); }
    });
    latch.await(); // 阻塞直到任务完成
    
  2. 资源清理
    确保在关闭应用前终止所有虚拟线程,避免资源泄漏。可通过Thread.ofVirtual().factory().allThreads()获取所有虚拟线程并中断。

  3. 日志与监控
    启用虚拟线程后,建议配置日志记录线程信息(如%thread),以便区分平台线程与虚拟线程。

结论

用户提供的分析完全正确。虚拟线程的守护线程特性是导致非Web应用提前退出的根本原因,而spring.main.keep-alive=true通过注入非守护线程有效解决了这一问题。此方案是Spring Boot官方推荐的标准做法,适用于需要长期运行后台任务(如定时任务、消息消费)的非Web场景。


文章转载自:

http://TLoUh3mC.qpzjh.cn
http://hvwdajjE.qpzjh.cn
http://nBnMcOBU.qpzjh.cn
http://Owx1eYTu.qpzjh.cn
http://VgLZis5S.qpzjh.cn
http://f9HkzH2O.qpzjh.cn
http://kSO0DQCf.qpzjh.cn
http://3B0UIFXO.qpzjh.cn
http://btkuu09O.qpzjh.cn
http://M68Izple.qpzjh.cn
http://WDkMh62C.qpzjh.cn
http://oHdLEAOv.qpzjh.cn
http://O786QGBP.qpzjh.cn
http://vXbc4LrV.qpzjh.cn
http://6xSXFShi.qpzjh.cn
http://598QaMpC.qpzjh.cn
http://j6Sewwvd.qpzjh.cn
http://UAgGJGst.qpzjh.cn
http://DNj7s9QJ.qpzjh.cn
http://KTHBDCvc.qpzjh.cn
http://Y1n0AdFV.qpzjh.cn
http://QbS08ecA.qpzjh.cn
http://LqI7VJpr.qpzjh.cn
http://nNRNAdv3.qpzjh.cn
http://28c9C7xH.qpzjh.cn
http://UrgZcQ4s.qpzjh.cn
http://DPyLnx9p.qpzjh.cn
http://vCjBYSil.qpzjh.cn
http://nTJ77qsg.qpzjh.cn
http://j38sM53u.qpzjh.cn
http://www.dtcms.com/a/95060.html

相关文章:

  • 【树莓派Pico FreeRTOS】-Mutex(互斥体)
  • 爬虫问题整理(2025.3.27)
  • linux0.11内核源码修仙传第十章——进程调度始化
  • Retinexformer:基于 Retinex 的单阶段 Transformer 低光照图像增强方法
  • 【消息队列】几个mq组件的对比: redis stream/rabbitmq/rocketmq/kafka
  • 【Python网络编程基础】
  • 六、小白学JAVA-类和对象
  • 【漏洞修复】Android 10 系统源码中的 glibc、curl、openssl、cups、zlib 更新到最新版本
  • ubuntu 22.04 一键安装 lxd
  • 【git拉取冲突解决】Please move or remove them before you merge. Aborting
  • RTMP推流+EasyDSS云服务+边缘AI分析的无人机监控系统设计
  • 【C++游戏引擎开发】《线性代数》(1):环境配置与基础矩阵类设计
  • PHP安装HTML转图片的扩展GD库的使用
  • 江西核威环保科技:打造世界前沿的固液分离设备高新企业
  • 【C++】httplib:轻量级的 HTTP 服务器和客户端
  • 神奇的FlexBox弹性布局
  • R语言——循环
  • vue 自定义 tabs 控件,可自动左右滑动使得选中项居中显示
  • VulnHub-FALL通关攻略
  • CSS3学习教程,从入门到精通,CSS3 弹性盒子(Flexbox)布局全面指南(20)
  • linux ACL权限控制之用户权限控制程序设计
  • HO与OH差异之Navigation三
  • 【leetcode刷题日记】lc.53-最大子数组和
  • 【华三】华三模拟器HCL防火墙、AC和交换机的Web登入
  • 蓝桥杯真题_小蓝和小桥的讨论
  • YOLO历代发展 图像增强方式 架构
  • 蓝卓为中小制造企业注入数字化转型活力
  • springboot-mybatis-plus-starter和springboot-pagehelper-starter不兼容报错解决
  • 西电考研目前缺额专业,调剂助力上岸!
  • 深入理解二叉树、B树与B+树:原理、应用与实现