JDK 24里程碑:虚拟线程重大升级,要用虚拟线程请务必用JDK24

JDK 24里程碑:虚拟线程重大升级,要用虚拟线程请务必用JDK24
🧑 博主简介ZEEKLOG博客专家历代文学网(PC端可以访问:https://literature.sinhy.com/#/?__c=1000,移动端可关注公众号 “ 心海云图 ” 微信小程序搜索“历代文学”)总架构师,16年工作经验,精通Java编程高并发设计分布式系统架构设计Springboot和微服务,熟悉LinuxESXI虚拟化以及云原生Docker和K8s,热衷于探索科技的边界,并将理论知识转化为实际应用。保持对新技术的好奇心,乐于分享所学,希望通过我的实践经历和见解,启发他人的创新思维。在这里,我希望能与志同道合的朋友交流探讨,共同进步,一起在技术的世界里不断学习成长。
🤝商务合作:请搜索或扫码关注微信公众号 “ 心海云图


文章目录

JDK 24里程碑:虚拟线程重大升级,要用虚拟线程请务必用JDK24

在这里插入图片描述

摘要

JDK 21引入的虚拟线程(Virtual Threads)革新了Java的并发编程模型,但其与最常用的同步机制synchronized之间存在一个关键冲突:线程固定(Pinning)。JDK 24通过的JEP 491提案从根本上解决了这一问题。

本文将通过时序图、流程图和代码示例,深入解析JDK 24如何重构synchronized的底层实现,使虚拟线程能够在不绑定平台线程的情况下安全地使用内置锁,从而释放虚拟线程的全部潜力。


一、 问题根源:虚拟线程与synchronized的先天冲突

要理解JDK 24的优化,首先必须清晰地定义问题所在。

1.1 虚拟线程的调度模型

虚拟线程的核心优势在于其高效的阻塞-恢复机制。其理想的工作流程如下:

虚拟线程VT-1载体线程CT-1操作系统/硬件VT-1挂载到CT-1并执行执行CPU计算发起阻塞式I/O请求(如读网络)I/O等待JVM卸载VT-1CT-1被释放,可执行其他VTI/O数据就绪JVM将VT-1挂载到CT-1(或CT-2)上继续执行后续代码虚拟线程VT-1载体线程CT-1操作系统/硬件

关键在于步骤4:当虚拟线程因I/O阻塞时,其载体线程(平台线程)会被立即释放。

1.2 synchronized 的固有壁垒

然而,synchronized关键字的语义是基于监视器锁(Monitor Lock) 的,而在JDK 24之前,这个锁的持有者是平台线程

考虑以下代码:

publicclassProblematicSync{privatefinalObject lock =newObject();publicvoidmethod(){synchronized(lock){// 平台线程在此获取监视器锁// 一些计算...readFromSocket();// 阻塞的I/O操作!// 更多计算...}}privatevoidreadFromSocket()throwsIOException{...}}

在JDK 21-23中,当虚拟线程执行上述方法时,会发生以下情况:

虚拟线程VT-1载体线程CT-1监视器锁 (lock)VT-1挂载到CT-1CT-1成功获取锁VT-1在同步块内执行调用readFromSocket(),发生I/O阻塞致命点:锁由CT-1持有JVM无法卸载VT-1!因为CT-1持有锁,它必须被“固定”(Pinned)。CT-1被阻塞,无法执行其他任务loop[空闲等待]I/O完成CT-1恢复执行VT-1继续执行同步块...CT-1释放锁虚拟线程VT-1载体线程CT-1监视器锁 (lock)

这种线程固定(Pinning) 现象使得一个宝贵的平台线程在虚拟线程被阻塞时完全闲置,与虚拟线程的设计目标背道而驰,严重制约了系统吞吐量。

为什么不能简单地让虚拟线程带着锁卸载?
因为这会引发极其复杂的线程所有权和内存可见性问题。如果VT-1在持有锁时被卸载,当它在另一个平台线程CT-2上恢复并释放锁时,JVM需要确保锁状态的一致性,这在旧的实现中几乎无法安全完成。


二、 JDK 24的解决方案:JEP 491详解

JEP 491: Synchronize Virtual Threads without Pinning 的目标是修改JVM,使虚拟线程能够在不固定平台线程的情况下使用synchronized

2.1 核心思想:锁所有权从平台线程转移到虚拟线程

JDK 24最根本的变革是:将监视器锁的持有者身份从“平台线程”提升为“虚拟线程”

现在,当虚拟线程进入synchronized块时,是虚拟线程本身被记录为锁的拥有者,而不是它当时恰好运行在之上的那个平台线程。

2.2 优化后的工作流程

让我们用同样的代码示例,看看在JDK 24中会发生什么:

publicclassFixedSync{privatefinalObject lock =newObject();publicvoidmethod(){synchronized(lock){// 虚拟线程VT-1本身获取锁readFromSocket();// 阻塞的I/O操作}}}

其执行流程如下图所示:

虚拟线程VT-1载体线程CT-1监视器锁 (lock)载体线程CT-2VT-1挂载到CT-1VT-1成功获取锁(锁所有者是VT-1)VT-1在同步块内执行调用readFromSocket(),发生I/O阻塞关键点:锁由VT-1持有,与CT-1无关。JVM安全地卸载VT-1CT-1被释放!CT-1去执行其他虚拟线程(如VT-2)I/O完成VT-1进入就绪队列JVM将VT-1挂载到空闲的CT-2上VT-1在CT-2上恢复执行由于它是锁所有者,直接继续执行同步块VT-1释放锁虚拟线程VT-1载体线程CT-1监视器锁 (lock)载体线程CT-2

这个流程清晰地展示了优化带来的巨大好处:

  1. 无固定(No Pinning):在I/O阻塞期间,载体线程CT-1被释放。
  2. 锁语义不变:锁的互斥性得到完全保证。在VT-1持有锁的整个期间,任何其他线程(虚拟或平台)都无法进入该同步块。
  3. 透明度高:开发者无需修改任何代码,即可享受性能提升。
2.3 技术实现浅析

JEP 491没有规定具体的实现方式,但可以推测JVM团队需要对对象头中的锁标记、锁记录等底层数据结构进行调整,以支持“虚拟线程作为锁所有者”这一新概念。这涉及到JVM中最复杂和敏感的部分,其改动是深远的。


三、 代码示例与影响分析

3.1 性能对比演示

以下是一个简单的演示,用于对比优化前后的差异。由于我们无法模拟JVM底层实现,此代码主要用于示意逻辑。

importjava.util.concurrent.Executors;importjava.util.stream.IntStream;publicclassSynchronizedVirtualThreadDemo{privatefinalObject lock =newObject();privateint counter =0;// 一个在同步块内包含模拟I/O的方法publicvoidsynchronizedOperation(String taskId){synchronized(lock){System.out.println(Thread.currentThread()+" - Task "+ taskId +" started."); counter++;// 模拟一个阻塞操作(如数据库查询、网络调用)simulateBlockingIO();System.out.println(Thread.currentThread()+" - Task "+ taskId +" finished. Counter: "+ counter);}}privatevoidsimulateBlockingIO(){try{// 休眠模拟I/O阻塞Thread.sleep(1000);}catch(InterruptedException e){Thread.currentThread().interrupt();}}publicstaticvoidmain(String[] args)throwsInterruptedException{var demo =newSynchronizedVirtualThreadDemo();long startTime =System.currentTimeMillis();try(var executor =Executors.newVirtualThreadPerTaskExecutor()){// 提交100个任务var futures =IntStream.range(0,100).mapToObj(i -> executor.submit(()-> demo.synchronizedOperation("T"+ i))).toList();// 等待所有任务完成for(var future : futures){ future.get();}}catch(Exception e){ e.printStackTrace();}long duration =System.currentTimeMillis()- startTime;System.out.println("Total time: "+ duration +" ms");}}

在JDK 21-23下的可能结果:
由于线程固定,每个任务都会在持有锁时固定一个平台线程。即使有大量虚拟线程,它们也会在锁外排队。执行时间会远大于1000毫秒,因为平台线程数量有限(默认为CPU核心数),任务只能串行化执行。

在JDK 24下的可能结果:
虚拟线程在synchronized块内阻塞时不会固定平台线程。当一个任务在锁内休眠时,它占用的平台线程会被释放去执行其他在锁外等待的任务。虽然锁的互斥性保证了counter的正确性,但平台线程的利用率极高。总执行时间会大幅缩短,接近 (1000ms * 任务批次数),呈现出极高的并发度。

3.2 对开发者的意义
  1. 简化决策:在JDK 24及以上版本中,可以更放心地使用synchronized。无需再仅仅因为虚拟线程而刻意将其替换为ReentrantLock
  2. 遵守最佳实践:应回归到锁选择的经典原则:优先使用synchronized,除非你需要ReentrantLock提供的超时、可中断、公平性等高级功能。
  3. 平滑迁移:为现有大量使用synchronized的代码库迁移到虚拟线程模型扫清了最大的障碍。

结论

JDK 24中对虚拟线程与synchronized协同工作的优化,是一次至关重要的底层修缮。它通过将锁的持有者从平台线程转移到虚拟线程,巧妙地解决了“线程固定”这一核心矛盾,使得虚拟线程的高效性与语言核心的同步机制得以完美结合。

这项改动看似透明,无需开发者干预,但其影响是深远的。它标志着Java并发编程在虚拟线程时代真正走向成熟,为构建下一代高吞吐、高可伸缩的Java应用奠定了坚实可靠的基础。对于开发者而言,这意味着我们可以更专注于业务逻辑的并发设计,而减少对底层同步机制选择的纠结,从而最大化地享受虚拟线程带来的技术红利。

Read more

MySQL 从入门到精通完全教程

目录 1. 前言 2. MySQL 基础认知 3. MySQL 安装与配置 4. MySQL 核心语法 5. 高级查询技巧 6. MySQL 函数 7. 数据约束 8. 事务管理 9. 索引优化 10. 存储过程与函数 11. 用户与权限管理 12. 性能优化实战 13. 常见问题与解决方案 1. 前言 1.1 什么是MySQL? MySQL 是一款开源的关系型数据库管理系统(RDBMS),基于SQL(结构化查询语言)实现数据管理,广泛应用于Web开发(如PHP+MySQL、Python+MySQL),特点是轻量、高效、跨平台、

By Ne0inhk
MySQL 进阶:库与表的DDL核心操作全指南(含实战案例)

MySQL 进阶:库与表的DDL核心操作全指南(含实战案例)

🔥草莓熊Lotso:个人主页 ❄️个人专栏: 《C++知识分享》《Linux 入门到实践:零基础也能懂》 ✨生活是默默的坚持,毅力是永久的享受! 🎬 博主简介: 文章目录 * 前言: * 一. 数据库(库)的核心操作 * 1.1 创建数据库:指定字符集与校验规则 * 1.1.1 语法格式 * 1.1.2 实战案例 * 1.2 字符集与校验规则:影响查询和排序 * 1.2.1 查看系统默认配置 * 1.2.2 查看支持的字符集和校验规则 * 1.2.3 校验规则的实际影响 * 1.3 操纵数据库:查询、修改、

By Ne0inhk
SpringBoot 整合 Langchain4j 实现会话记忆存储深度解析

SpringBoot 整合 Langchain4j 实现会话记忆存储深度解析

目录 一、前言 二、AI大模型会话记忆介绍 2.1 AI 大模型的会话记忆是什么 2.2 AI 大模型为什么需要会话记忆 2.3 AI 大模型会话记忆常用实现方案 2.4 LangChain4j 会话记忆介绍 2.4.1 LangChain4j 会话记忆介绍 2.4.2 LangChain4j 会话记忆类型 三、Langchain4j 会话记忆操作案例使用 3.1 前置准备 3.1.1 导入依赖文件 3.1.2 添加配置文件 3.1.3 前置案例 3.

By Ne0inhk