内容提要
Jactl基准测试显示,Java 21上Vert.x事件循环吞吐量比虚拟线程高2.5倍,而Java 25上虚拟线程反超。差异源于虚拟线程挂载开销及调度器优化。选择取决于应用架构和编程范式,非纯性能问题。
延伸解读
基准测试的局限
Jactl基准测试仅测了吞吐量,未测延迟。在事件循环模型中,线程数固定,高并发下队列可能变长,导致延迟上升;而虚拟线程可创建大量线程,延迟表现可能不同。测试环境为18核MacBook Pro,若换低核数或更高并发,结果可能改变。因此,选择并发模型时,不能仅看吞吐量,还需考虑延迟和实际部署环境。
虚拟线程的演进
Java 21到Java 25,虚拟线程性能大幅提升,主要得益于调度器优化和解决“钉住”问题。但虚拟线程仍在快速演进,例如有报告称JDK 25上Thread.sleep(1)偶尔耗时长达60秒,虽已修复,但表明其性能尚未完全稳定。开发者应关注后续版本改进,并测试自身应用场景。
架构决定选择
文章强调,选择虚拟线程还是事件循环,更多取决于应用架构和编程范式,而非纯性能。若现有Vert.x应用运行良好,重构成虚拟线程可能不划算;若可升级到Java 25且追求更高吞吐,虚拟线程值得考虑。Jactl脚本本身不关心底层机制,但承载它的Java应用需在响应式回调与同步阻塞风格间做选择。
Q&A
Jactl基准测试中,Java 21和Java 25上虚拟线程与Vert.x事件循环的吞吐量对比结果如何?
在Java 21上,当脚本包含10次10毫秒的模拟阻塞操作时,Vert.x事件循环方案的吞吐量是虚拟线程方案的2.5倍;而在Java 25上,虚拟线程方案在所有带阻塞的场景下都反超了Vert.x方案。
为什么在Java 21上虚拟线程的性能不如事件循环?
因为虚拟线程每次阻塞操作都需要进行挂起和恢复,涉及保存栈帧、释放载体线程、重新调度等开销。而Jactl的Continuation机制通过抛异常捕获执行状态,在Java 21上比虚拟线程的完整挂载/卸载流程更轻量,因此事件循环方案性能更好。
Java 25对虚拟线程做了哪些优化使其性能反超?
Java 25引入了基于ForkJoinPool的Mounting Scheduler,采用两级调度模型,降低了载体线程的创建和销毁开销,CPU使用率降低约5%-10%。此外,Java 24解决了synchronized导致虚拟线程被钉住的问题,这些改进大幅降低了虚拟线程的挂起/恢复开销。
虚拟线程和事件循环在编程范式上有什么本质区别?
事件循环要求事件处理器永不阻塞,所有耗时I/O必须写成非阻塞回调,代码碎片化且调试困难;虚拟线程允许开发者使用同步阻塞的写法,由JVM在底层自动挂起和恢复,代码更自然、顺序化,易于理解和维护。
根据Jactl作者的建议,在什么情况下应该选择虚拟线程或事件循环?
如果使用Java 21之前的版本,只能选择Jactl/Vert.x;如果已在Java 21上使用Vert.x,不建议重构成虚拟线程,因为性能更好;如果升级到Java 25,虚拟线程吞吐量更高,可以考虑切换。最终选择应基于应用架构和编程范式,而非纯性能。
Jactl基准测试存在哪些局限性?
测试只测量了吞吐量,未测量延迟(如P99响应时间);测试环境为18核MacBook Pro M5 Max,未覆盖低核数或极端高并发场景;阻塞时长固定为10毫秒,未测试其他时长。因此结果可能不适用于所有场景。