支持JDK19虚拟线程的web框架,之三:观察运行中的虚拟线程


欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos

《支持JDK19虚拟线程的web框架》系列文章链接

  • 支持JDK19虚拟线程的web框架,之一:体验
  • 支持JDK19虚拟线程的web框架,之二:完整开发一个支持虚拟线程的quarkus应用
  • 支持JDK19虚拟线程的web框架,之三:观察运行中的虚拟线程
  • 支持JDK19虚拟线程的web框架,之四:看源码,了解quarkus如何支持虚拟线程
  • 支持JDK19虚拟线程的web框架,之五(终篇):兴风作浪的ThreadLocal

本篇概览

  • 本篇是《支持JDK19虚拟线程的web框架》系列的第三篇,在前面两篇咱们一起了解和体验了支持虚拟线程的web服务,功能性能都试过,整个开发过程也完整执行,算是对quarkus和虚拟线程有了初步的了解,但也留下两个问题
  1. 虚拟线程和常规子线程的区别,究竟能不能看出来?前文已经验证了性能上区别不大,那还有别的方式来观察和区分吗?
  2. 能不能稍微深入一点,仅凭一个@RunOnVirtualThread注解就强行写两篇博客,实在是太忽悠人了
  • 本文聚焦第一个问题,与大家一起深入了解虚拟线程,重点在理论结合实际,将官方资料在实战中得到印证
  • 至于第二个问题,留待下一篇...

设置

  • 开始深入学习前有个设置需要确认,否则会导致访问服务失败,请打开开发的quarkus应用,下图红色箭头指向的配置必须存在,且值必须是0.0.0.0
image-20221022153432270
  • 如果没有上述配置,IDEA启动的应用就只会监听127.0.0.1这块网卡,如此依赖外部测试工具访问此应用的服务就无法成功
  • 那么就开始吧:如何直观地、清楚地看出虚拟线程和常规子线程的区别?

准备工作

  • 工欲善其事.....咱们先把必要的工具装上:IDEA的JProfiler插件,安装步骤如下图

image-20221015164110766

  • 接下来请在自己电脑上安装JProfiler,注意,这一步必须要做,详细的安装和注(po)册(jie)过程就不写在本文中了,请自行搜索相关资料
  • 完成上述准备后,点击下图箭头所指按钮,这样就指定了JProfiler去监控分析启动后的应用进程
  • IDEA会拉起JProfiler
image-20221022114521069
  • 出现新的窗口如下图,再点击右下角的确定按钮
image-20221022114723561
  • 现在JProfiler已经在监控quarkus应用的进程了,界面如下
image-20221022120025891
  • 如下图,点击线程历史菜单,就能看到当前应用进程内的所有线程,注意按照步骤2过滤一下,只看存活的线程
image-20221022160249366
  • 接下来,咱们就要用JProfiler来观察常规线程和虚拟线程的区别了
  • 先回忆一下,前文中,咱们开发的quarkus应用有两个web服务类,分别是:
  1. VTPersonResource.java,该服务类使用了虚拟线程来执行web响应,对应web路径:/vt/persons

  2. PoolPersonResource.java,该服务类未使用虚拟线程,所以执行web响应的是传统线程池中的子线程,对应web路径:/pool/persons

  • 接下来,压测工具k6先后压测上述两个接口,用JProfiler观察进程中线程的变化情况

不使用虚拟线程时的线程状况

  • 咱们先发请求到/pool/persons,也就是先不用虚拟线程,看看传统线程池响应web服务的时候,在JProfiler中是啥样的
  • 像java官方文档,找到虚拟线程定义的那段,如下图,注意红框中的内容以及我的中文注解(我将下面这幅图称为本篇最有价值的地方)

image-20221022175154277

  • 没错,官方文档虽多,但咱们没必要全看,上面这段才是关键,看完后捋捋流程图如下
image-20221022212357583
  • 看到上图,您应该会有以下三个疑惑:
  1. ForkJoin线程池啥时候创建的?会不会销毁?
  2. 调度器(scheduler)啥时候创建的?会不会销毁?
  3. carrier啥时候创建的?会不会销毁?
  • 如果这些关键问题没说清楚,上面的流程图算不算是捋了个寂寞...
  • 要想搞清楚为什么没有回答上面三个问题,咱们把官方文档滚动到最顶部,如下图
image-20221022213416135
  • 上图红框表明,这是一篇JEP文档,即: JDK Enhancement Proposal ,这类文档只提出标准,而非实现,真正实现的这个标准的,是各个JVM虚拟机厂家(例如Oracle),所以,要想回答上面三个问题,只能去查找具体JDK软件的实现
  • 简单的说:别纠结那三个问题,我答不上来...
  • 咱们继续,接下来更精彩
  • 看过官方资料后,再回到最初的问题,咱们想通过JProfiler得到什么?相信您已经很清楚了吧,我觉得是这三样:
  1. 调度器,scheduler(ForkJoin线程池中的线程)
  2. 执行虚拟线程任务的真实线程,carrier
  3. 虚拟线程
  • 现在开始压测吧,继续用k6,如下图,脚本中的地址要改成使用虚拟线程的web服务
image-20221022171002446
  • 压测期间去观察JProfiler,如下图,完全符合预期,说实话,第一次看到这些内容时,自己的内心是很激动的,这种知识点得到印证的感觉真是太好了

image-20221022215116960

  • 再看看那些不再存活的线程,如下图,大量VirtualThreads存在,这也符合虚拟线程的特性:不复用,执行完毕就结束

image-20221022220055370

  • 等到压测结束后,scheduler、carrier、虚拟线程,它们都不再存活,如下图
image-20221022221215621
  • 如此看来,在执行任务的时候,会出现sheduler和carrier来完成虚拟线程中的任务,等到这些任务执行完毕,所有真实线程、虚拟线程都被结束,不再存活
  • 至此,借助JProfiler观察常规线程和虚拟线程的实战就完成了,经过了这些理论结合实际的操作和分析,相信您对虚拟线程的认知已经更具体和全面,如今它不再神秘或者高深莫测,咱们也更有信心学好它用好它

我有个想法

  • 码字码到这里,我想抛出一个大胆的想法和大家一起讨论:今天咱们借助JProfiler观察到了scheduler、carrier、虚拟线程等的创建、运行、结束等过程,我这里用的虚拟机是azul JDK,所以JProfiler中看到的也只是azul JDK对虚拟线程规范的实现情况,如果换成其他JDK,例如Oracle JDK,那么在JProfiler中看到的scheduler、carrier、虚拟线程它们会不会有所不同呢?(例如scheduler可能会存活得久一些)毕竟JEP 425只是个标准,没有明确规定实现,而azul JDK和Oracle JDK属于不同厂商的实现
  • 当然了这只是个猜测,篇(lan)幅(de)所(dong)限(shou)就不在本篇做这些事情了,当我相信会有爱动手的读者去实战操作的,麻烦您告诉欣宸一下您的验证结果,谢谢啦!
  • 写到这里,虚拟线程的文章可以完结了吗?不会,接下来咱们还要畅游quarkus,揭秘@RunOnVirtualThread注解背后的故事,看看优秀的框架是如何玩转虚拟线程的,上广告词:欣宸原创,不辜负您的期待!

欢迎关注博客园:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...