Tomcat 配合虚拟线程,一种新的编程体验
Java 21 在今年早些时候的 9 月 19 日就正式发布,并开始正式引入虚拟线程,但是作为 Java 开发生态中老大哥 Spring 并没有立即跟进,而是在等待了两个月后的 11 月 29 日,伴随着 Spring Boot 3.2 版本的发布,在这个版本中也终于是引入了对虚拟线程的支持。
虚拟线程的引入标志着 Java 在现代编程世界中对编写高吞吐量、高并发应用程序提供了更加完美的支持。
本文我就带着大家一起深入了解一波 Tomcat 配合虚拟线程会带来怎样的效果以及虚拟线程对以后使用 Java 开发高吞吐量、高并发应用程序时所带来的改变。
本文大纲如下,

Tomcat 使用虚拟线程
启用虚拟线程
在 Spring Boot 3.2 中,使用 Tomcat 作为 web 容器时,启用虚拟线程只需要将 spring.threads.virtual.enabled 属性设置为 true。
这样 Spinrg Boot 在启动 Tomcat 容器时会使用一个虚拟线程执行器来代表原有的平台线程池。
注意这里是虚拟线程执行器,不是虚拟线程池哦。
源码解析
在 Spring Boot 3.2 版本以前,Tomcat 默认的线程池使用的就是 Java 提供的 ThreadPoolExecutor 线程池,在 3.2 版本以后,Spring Boot 修改了创建线程池的方法如下所以,


可以看到 Tomcat 会先判断是否启用了虚拟线程,启用了的话就直接创建一个虚拟线程执行器 VirtualThreadExecutor。
VirtualThreadExecutor 类是 Tomcat 为了使用虚拟线程作为执行器而新增的。他的内部代码中针对每个请求任务都是依赖 Jre21Compat 类处理的。
Jre21Compat 类则是 Tomcat 为了兼容 Java21 版本虚拟线程新增的一个兼容类。这个类利用反射方法来调用 Thread.ofVirtual().start(() -> {}) 方法,以便进行任务处理,代码截图如下,


虽然以上代码可以启用 Tomcat 的虚拟线程支持。但是在 Spring Boot 中其实不是这样设置的。还记得上文提到的在 Spring Boot 3.2 中,使用 Tomcat 作为 web 容器时,启用虚拟线程只需要将 spring.threads.virtual.enabled 属性设置为 true 吗?
Spring Boot 3.2 中是通过 tomcatVirtualThreadsProtocolHandlerCustomizer 方法来兼容虚拟线程启用逻辑的,@ConditionalOnThreading(Threading.VIRTUAL) 条件用判断 spring.threads.virtual.enabled 属性是否启用。代码如下,


到这里其实本文所需要讲的涉及源码的部分就全部讲完了。可以看到 Tomcat 引入虚拟线程并不复杂,引入后不在需要维护线程池,减轻了执行器的复杂度。
虚拟线程带来的改变
不知道大家注意到源码中一个改变没有,就是在 Spring Boot 3.2 中,启用了虚拟线程后,Tomcat 默认使用的虚拟线程执行器不在需要池化。
也就是说,在 Spring Boot 3.2 以后的版本里,我们不在需要设置 server.tomcat.threads.max 以及 server.tomcat.threads.min-spare 两个属性以控制 Tomcat 线程池的大小了,因为它压根没有使用平台线程池。
对于 Tomcat 来说,引入虚拟线程,不必在为线程池的维护而费心,还能减轻编程的复杂度。
虚拟线程由 JVM 平台负责进行调度,它是廉价且轻量级的,Tomcat 可以使用 “每个请求一个线程” 模型,而不必担心实际需要多少个线程。
就算请求任务在虚拟线程中调用阻塞 I/O 操作,导致运行时虚拟线程被挂起阻塞,但是只要挂起结束后该虚拟线程就可以恢复。
使用了虚拟线程后,程序员使用普通的阻塞 API,也可以让程序对硬件的利用达到近乎完美水平,以此提供高水平的并发性,从而实现高吞吐量。
可以说,虚拟线程的引入,以后程序员就算是使用 Java 中阻塞 API 也可以开发出高性能、高吞吐量的应用程序。
jmter 实测
在本文中,我还将给各位展示一波 newbeepro 项目升级到 Spring Boot 3.2 后启用虚拟线程所带来的性能提升。
测试服务器
- 主机名称 VM-16-5-centos
- 发行版本 centos-7.9.2009
- 内核版本 3.10.0-1160.88.1.el7.x86_64
- 系统类型 x86_64
- 系统配置:2 核 4 G 5M 带宽

测试项目
newbee-mall-pro 是 newbee-mall 商城的 pro 版本实现了推荐算法、商品秒杀、优惠卷使用,滑块验证码,支付宝支付,中文分词检索等高级功能。
测试方法
使用 newbee-mall-pro 作为测试项目将启用虚拟线程以及未启用虚拟线程的两次设置部署到测试服务器上。
启动容器:amazoncorretto:21.0.1
启动参数:java -jar -Xms1024m -Xmx1024m /opt/newbeemall/newbee-mall.jar
部署后测试地址:http://62.234.206.94/newbeemall/index
测试接口为秒杀接口:/newbeemall/seckill/2/c81e728d9d4c2f636f067f89cc14862c/executionFour
压测设置:启用 2000 个线程,每个线程循环执行 30 秒左右。一共测试五轮,先预热 JVM 后,取吞吐量最大值。
测试数据
启用虚拟线程
压测结果如下,


可以看到 CPU 占用达到百分之 142,内存占用达到百分之 35 的情况下,压测吞吐量最大可以达到 1731。
不启用虚拟线程
考虑到有 2000 个线程进行压测,所以将 Tomcat 线程池的最大线程数也设置到 2000,如下图,

压测结果如下,


可以看到 CPU 占用达到百分之 170,内存占用达到百分之 35 的情况下,压测吞吐量可以达到 1492。
OK,到这里我们可以看到在 Spring Boot 3.2 版本中,使用了虚拟线程的 Tomcat 对比不用虚拟线程时,吞吐量提升差不多有 20%。
在更高并发的测试中,这个差距会越来越明显。因为 Tomcat 使用的平台线程过多时,上下文切换开销会越来越大,而且虚拟线程比平台线程占用更少的内存,一个虚拟线程只占用几 kb 到几十 kb 内存。可以轻松创建上万虚拟线程,降低资源占用同时提高并发。
最后聊两句
虚拟线程带给了现代程序员新的编程体验,使用阻塞编程也能开发出高性能应用程序,而避免了异步模型的编程复杂度,随着更多的框架接入虚拟线程,相信虚拟线程会在未来大放异彩。
关注公众号【waynblog】每周分享技术干货、开源项目、实战经验、国外优质文章翻译等,您的关注将是我的更新动力!
Tomcat 配合虚拟线程,一种新的编程体验的更多相关文章
- 在Tomcat中启用虚拟线程特性
前提 趁着国庆前后阅读了虚拟线程相关的源码,写了一篇<虚拟线程 - VirtualThread源码透视>,里面介绍了虚拟线程的实现原理和使用示例.需要准备做一下前期准备: 安装OpenJD ...
- Tomcat之虚拟主机配置以及web应用配置
Tomcat之虚拟主机配置以及web应用配置 Tomcat文件夹结构例如以下: bin ---- 启动和关闭须要的bat文件所在的文件夹 conf --- 配置文件夹 lib --- tomcat执 ...
- 虚拟线程 - VirtualThread源码透视
前提 JDK19于2022-09-20发布GA版本,该版本提供了虚拟线程的预览功能.下载JDK19之后翻看了一下有关虚拟线程的一些源码,跟早些时候的Loom项目构建版本基本并没有很大出入,也跟第三方J ...
- JSP之WEB服务器:Apache与Tomcat的区别 ,几种常见的web/应用服务器
注意:此为2009年的blog,注意时效性(针对常见服务器) APACHE是一个web服务器环境程序 启用他可以作为web服务器使用 不过只支持静态网页 如(asp,php,cgi,jsp)等 ...
- 1.tomcat部署项目的几种方式和weblogic部署方式及一点通讯
第一种部署方式: 直接使用myeclipse 找到server服务 添加要部署的项目Add Deployment ,然后选中某个项目,首选Exploded Archive(development ...
- tomcat建立虚拟主机
WEB浏览器与WEBserver建立连接后,除了将请求URL中的资源路径发送给WEBserver外,还会将URL中的主机名部分作为HTTP请求消息的Host头发送给WEBserver.比如,在浏览器地 ...
- tomcat关闭后线程依然运行解决办法
tomcat关闭后线程依然运行解决办法,设置线程为守护线程 守护线程与非守护线程 最近在看多线程的Timer章节,发现运用到了守护线程,感觉Java的基础知识还是需要补充. Java分为两种线程:用户 ...
- 详解tomcat连接数和线程数
前言 在使用tomcat时,经常会遇到连接数.线程数之类的配置问题,要真正理解这些概念,必须先了解Tomcat的连接器(Connector). 在前面的文章 详解Tomcat配置文件server.xm ...
- Tomcat 连接数与线程池详解
前言 在使用tomcat时,经常会遇到连接数.线程数之类的配置问题,要真正理解这些概念,必须先了解Tomcat的连接器(Connector). 在前面的文章 详解Tomcat配置文件server.xm ...
- Java将增加虚拟线程,挑战Go协程
我们知道 Go 语言最大亮点之一就是原生支持并发,这得益于 Go 语言的协程机制.一个 go 语句就可以发起一个协程 (goroutin).协程本质上是一种用户态线程,它不需要操作系统来进行调度,而是 ...
随机推荐
- SpringBoot项目统一处理返回值和异常
目录 简介 前期准备 统一封装报文 统一异常处理 自定义异常信息 简介 当使用SpringBoot开发Web项目的API时,为了与前端更好地通信,通常会约定好接口的响应格式.例如,以下是一个JSON格 ...
- 浅谈 Linux 下 vim 的使用
Vim 是从 vi 发展出来的一个文本编辑器,其代码补全.编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用. Vi 是老式的字处理器,功能虽然已经很齐全了,但还有可以进步的地方.Vim 可 ...
- Go 并发编程 - Goroutine 基础 (一)
基础概念 进程与线程 进程是一次程序在操作系统执行的过程,需要消耗一定的CPU.时间.内存.IO等.每个进程都拥有着独立的内存空间和系统资源.进程之间的内存是不共享的.通常需要使用 IPC 机制进行数 ...
- Vue【原创】日历组件Calendar
最近项目中封装了一个日历组件,用于节假日管理,支持输入默认选中的日期,选择管理日期. 效果图: calendar组件: 1 <template> 2 <div class=" ...
- datetime去除时分秒
datetime.datetime.now().replace(microsecond=0)
- shell、python时间函数小结
有时需要写一些定时任务脚本,简单总结一下,备忘. 1. 获取当前时间 python 在windows下精确到0.001秒,linux下时间精度为0.000001秒 >>> impor ...
- Spring中事务的传播行为有哪些?
Spring中事务的传播行为有哪些? 现在我们来谈一个场景,再来引出事务传播行为这个概念.现在有methodA( ) 和 methodB( ),而且两个方法都显示的开启了事务,那么methodB( ) ...
- springboot、jvm调优(设置运行的参数)
1.工具 jdk自带的工具位置: 找到窗口->应用程序 2.问题和方式 在SpringBoot项目中,调优主要通过配置文件和配置JVM的参数的方式进行. 2.1 springboot修改配置文件 ...
- python实现简单的爬虫功能
前言Python是一种广泛应用于爬虫的高级编程语言,它提供了许多强大的库和框架,可以轻松地创建自己的爬虫程序.在本文中,我们将介绍如何使用Python实现简单的爬虫功能,并提供相关的代码实例. 如何实 ...
- .NET开发工作效率提升利器 - CodeGeeX AI编程助手
前言 2022年6月,随着GitHub Copliot正式面向大众发布.让许多开发者都感受到了AI辅助编程工具的魅力所在,Copilot实现了帮助开发者大大提高了编程开发效率,让程序员朝九晚五成为可能 ...