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).协程本质上是一种用户态线程,它不需要操作系统来进行调度,而是 ...
随机推荐
- elasticsearch中的数据类型search_as_you_type及查看底层Lucene索引
search_as_you_type字段类型用于自动补全,当用户输入搜索关键词的时候,还没输完就可以提示用户相关内容.as_you_type应该是说当你打字的时候.它会给索引里的这个类型的字段添加一些 ...
- Unity TextMeshPro 添加中文字体遇见的问题以及解决方案
前言 按标准官方教程为 Unity TextMeshPro 添加中文字体时出现了各种奇奇怪怪的问题,于是有了这篇随笔. 中文字体解决方案 以下步骤适用于 TextMeshPro 3.0.6. 字符数量 ...
- 在Windows下用VScode构造shell脚本的IDE
在linux系统中,大家可以很轻松的开发.调试shell脚本.但是,对于不熟悉linux系统 的小白或者想在Windows下开发shell脚本的人来说,这就有点不友好了.本篇文章就 教大家,在Wind ...
- 通过商品API接口获取到数据后的分析和应用
一.如果你想要分析商品API接口获取到的数据,可以按照如下的步骤进行: 了解API接口返回值的格式,如JSON格式.XML格式.CSV格式等,选择适合你的数据分析方式. 使用API请求工具(如Post ...
- WPF学习 - 闭坑(持续更新)
坑1:自定义控件设计原则: 既然称之为控件,那么就必定有界面与行为两部分. 界面就是展示给用户看的,用于承载类的属性.方法.事件等. 行为就是类的方法,以及这些方法需要用到的属性.字段等. WPF设计 ...
- Mybatis自动生成mapper和实体类
准备工作:需要俩个jar包: (1)连接数据库的jar包:mysql-connector-java-5.1.16-bin.jar (2)实现需求的插件Generator:mybatis-generat ...
- 《Hadoop大数据技术开发实战》新书上线
当今互联网已进入大数据时代,大数据技术已广泛应用于金融.医疗.教育.电信.政府等领域.各行各业每天都在产生大量的数据,数据计量单位已从B.KB.MB.GB.TB发展到PB.EB.ZB.YB甚至BB.N ...
- PowerShell收集信息及绕过PowerShell权限
PowerShell脚本的4种执行权限: Restricted:默认设置,不允许任何script运行 AllSigned:只能运行经过数字证书签名的script RemoteSigned:本地脚本不做 ...
- Scrapy官方文档爬取
最近想爬点啥东西看看, 所以接着学习了一点Scrapy, 学习过程中就试着去爬取Scrapy的官方文档作为练习之用, 现在已经基本完成了. 实现原理: 以 overview.html 为起点,通过 r ...
- vue2和vue3使用echarts时无数据,怎么显示暂无数据图片或文字
一开始也经历了用v-if和v-show,v-show的话echarts还会留出暂无数据图片的位置,导致echarts变形,v-if在加载和不加载切换时,dom会获取不到:后来也是在网上找的方法,时间有 ...