Spring Boot3虚拟线程的使用步骤详解

 更新时间:2025年03月29日 10:25:38   作者:招风的黑耳  
虚拟线程是 Java 19 中引入的一个新特性,旨在通过简化线程管理来提升应用程序的并发性能,这篇文章主要介绍了Spring Boot3虚拟线程的使用步骤,文中通过代码介绍的非常详细,需要的朋友可以参考下

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

问题根源分析

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

  • 虚拟线程的特性

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

解决方案验证

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

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

  • 线程终止逻辑当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退出,等待虚拟线程任务完成。

扩展建议

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

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

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

结论

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

到此这篇关于Spring Boot3虚拟线程的使用步骤详解的文章就介绍到这了,更多相关SpringBoot3虚拟线程使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java泛型extends关键字设置边界的实现

    Java泛型extends关键字设置边界的实现

    这篇文章主要介绍了Java泛型extends关键字设置边界的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-09-09
  • SpringBoot定制JSON响应数据的实现

    SpringBoot定制JSON响应数据的实现

    本文主要介绍了SpringBoot定制JSON响应数据的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-02-02
  • Java I/O中I/O流的典型使用方式详解

    Java I/O中I/O流的典型使用方式详解

    这篇文章主要介绍了Java I/O中I/O流的典型使用方式详解,尽管可以通过不同的方式组合IO流类,但我们可能也就只用到其中的几种组合。下面的例子可以作为典型的IO用法的基本参考,,需要的朋友可以参考下
    2019-06-06
  • Maven的聚合(多模块)和Parent继承

    Maven的聚合(多模块)和Parent继承

    今天小编就为大家分享一篇关于Maven的聚合(多模块)和Parent继承,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • Javabean基于xstream包实现转XML文档的方法

    Javabean基于xstream包实现转XML文档的方法

    这篇文章主要介绍了Javabean基于xstream包实现转XML文档的方法,结合具体实例形式分析了xstream包用于转换xml文件的具体使用技巧,需要的朋友可以参考下
    2017-05-05
  • Spark学习笔记之Spark SQL的具体使用

    Spark学习笔记之Spark SQL的具体使用

    这篇文章主要介绍了Spark学习笔记之Spark SQL的具体使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-06-06
  • SpringData JPA实体映射与关系映射的实现

    SpringData JPA实体映射与关系映射的实现

    Spring Data JPA作为Spring生态系统中的核心项目,通过JPA规范提供了优雅而强大的实体映射与关系处理机制,下面就来介绍一下,感兴趣的可以了解一下
    2025-04-04
  • Java BigDecimal和double示例及相关问题解析

    Java BigDecimal和double示例及相关问题解析

    这篇文章主要介绍了Java BigDecimal和double示例及相关问题解析,简单介绍了BigDecimal类的相关内容,分享了两则相关实例,对问题进行了分析,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • 解析Java和IDEA中的文件打包问题

    解析Java和IDEA中的文件打包问题

    这篇文章主要介绍了Java和IDEA中的文件打包问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-07-07
  • Java中的内存模型JMM详细解读

    Java中的内存模型JMM详细解读

    这篇文章主要介绍了Java中的内存模型JMM详细解读,Java 对内存的抽象模型如下,每个线程都有一块自己的私有内存(也称为工作内存),当线程使用变量时,会把主内存里面的变量复制到工作内存,线程读写变量时操作的是自己工作内存中的变量,需要的朋友可以参考下
    2023-12-12

最新评论