欢迎来到徐庆高(Tea)的个人博客网站
磨难很爱我,一度将我连根拔起。从惊慌失措到心力交瘁,我孤身一人,但并不孤独无依。依赖那些依赖我的人,信任那些信任我的人,帮助那些给予我帮助的人。如果我愿意,可以分裂成无数面镜子,让他们看见我,就像看见自己。察言观色和模仿学习是我的领域。像每个深受创伤的人那样,最终,我学会了随遇而安。
当前位置: 日志文章 > 详细内容

SpringBoot实现虚拟线程的方案

2025年08月05日 Java
什么是虚拟线程虚拟线程是java19开始增加的一个特性,和golang的携程类似,一个其它语言早就提供的、且如此实用且好用的功能,作为一个java开发者,早就已经望眼欲穿了。虚拟线程和普通线程的区别&

什么是虚拟线程

虚拟线程是java19开始增加的一个特性,和golang的携程类似,一个其它语言早就提供的、且如此实用且好用的功能,作为一个java开发者,早就已经望眼欲穿了。

虚拟线程和普通线程的区别

“虚拟”线程,望文生义,它是“假”的,它不直接调度操作系统的线程,而是由jvm再提供一层线程的接口抽象,由普通线程调度,即一个普通的操作系统线程可以调度成千上万个虚拟线程。

虚拟线程比普通线程的消耗要小得多得多,在内存足够的情况下,我们甚至可以创建上百万的虚拟线程,这在之前(java19以前)是不可能的。

其实如果有用过akka的朋友们会发现,其实两者很相似,只不过使用akka是应用程序来处理,而虚拟线程是jvm来处理,使用上更简洁且方便。

springboot使用虚拟线程

下面我们会在springboot中使用虚拟线程,将默认的异步线程池和http处理线程池替换为虚拟线程,然后对比虚拟线程和普通线程的性能差异,你会发现差别就像马车换高铁,不是一个时代的东西。

配置

首先我们使用的java版本是java-20.0.2-oracle,springboot版本是3.1.2。

要在springboot中使用虚拟线程很简单,增加如下配置即可:

/**
 * 配置是用于稍后测试,spring.virtual-thread=true是使用虚拟线程,false时还是使用默认的普通线程
 */
@configuration
@conditionalonproperty(prefix = "spring", name = "virtual-thread", havingvalue = "true")
public class threadconfig {

    @bean
    public asynctaskexecutor applicationtaskexecutor() {
        return new taskexecutoradapter(executors.newvirtualthreadpertaskexecutor());
    }

    @bean
    public tomcatprotocolhandlercustomizer<?> protocolhandlercustomizer() {
        return protocolhandler -> {
            protocolhandler.setexecutor(executors.newvirtualthreadpertaskexecutor());
        };
    }
}

@async性能对比

我们写一个异步service,里面睡眠50ms,模拟mysql或redis等io操作:

@service
public class asyncservice {

    /**
     * 
     * @param countdownlatch 用于测试
     */
    @async
    public void dosomething(countdownlatch countdownlatch) throws interruptedexception {
        thread.sleep(50);
        countdownlatch.countdown();
    }
}

最后测试类,很简单,就是循环调用这个方法10万次,计算所有方法执行完成的消耗的时间:

@test
public void testasync() throws interruptedexception {
    long start = system.currenttimemillis();
    int n = 100000;
    countdownlatch countdownlatch = new countdownlatch(n);
    for (int i = 0; i < n; i++) {
        asyncservice.dosomething(countdownlatch);
    }
    countdownlatch.await();
    long end = system.currenttimemillis();
    system.out.println("耗时:" + (end - start) + "ms");
}

普通线程耗时:678秒左右,超过10分钟了

虚拟线程耗时:3.9秒!!

朋友们,接近200倍的性能差距!!

http请求性能对比

让我们再看看http请求的对比,简单写个get请求,里面什么也不做,一样睡50ms,模拟io操作:

@requestmapping("/get")
public object get() throws exception {
    thread.sleep(50);
    return "ok";
}

然后我们使用jmeter请求接口,500个并发线程,运行1万次,看看效果如何:

「普通线程:」

可以看到最小用时50ms,这个没毛病,接口里面睡眠了50ms,但是不管是中位数还是90/95/99线都大于150ms了,这是因为系统线程是一个很昂贵的资源,springboot中tomcat默认的最大连接数应该是200,在连接池的线程被耗尽后,这200个线程在那干等50ms结束,而剩下的请求也只能等待,无法进行其它的操作。下面再看下虚拟线程的表现:

「虚拟线程耗时:」

可以看到即使是最大耗时,也保持在100ms以下,即线程等待时间显著的减少,虚拟线程更好的利用了系统资源。

总结

从上面的性能对比来看,虚拟线程在性能方面有明显的优势,但是要注意的是,我们上面的测试都是让线程等待了50ms,这是模拟什么场景?

没错,是io密集型场景,即线程大部分时间是在等待io,这样虚拟线程才可以发挥出它的优势,如果是cpu密集型场景,那么可能效果并不大。不过我们目前大部分的应用都是io密集型应用较多,比如典型的web应用,大量的时间在等待网络io(db、缓存、http等等),使用虚拟线程的效果还是非常明显的。

最后:大部分的公司可能还在用java8,但是我想说的是,是时候升级了,跟上时代的脚步吧,朋友们!

到此这篇关于springboot实现虚拟线程的方法步骤的文章就介绍到这了,更多相关springboot 虚拟线程内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!