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

飞书 × OpenClaw 接入指南:不用服务器,用长连接把机器人跑起来

你想在飞书里用上一个能稳定对话、能发图/收文件、还能按规则在群里工作的 AI 机器人,最怕两件事:步骤多、出错后不知道查哪里。这个项目存在的意义,就是把“飞书接 OpenClaw”这件事,整理成一套对非技术也友好的配置入口,并把官方文档没覆盖到的坑集中写成排查清单。 先说清楚它的角色:OpenClaw 现在已经内置官方飞书插件 @openclaw/feishu,功能更完整、维护也更及时。这是好事,说明飞书 + AI 的接入已经走通。这个仓库并不是要替代官方插件,而是继续为大家提供: * 新用户:从零开始的新手教程(15–20 分钟) * 老用户:从旧版(独立桥接或旧 npm 插件)迁移到官方插件的保姆级路线 * 常见问题答疑 & 排查清单(最常见的坑优先) * 进阶场景:独立桥接模式依然可用(需要隔离/定制时再用) 另外,仓库也推荐了一个新项目

By Ne0inhk
Java 大视界 -- Java 大数据在智能家居环境监测与智能调节中的应用拓展(423)

Java 大视界 -- Java 大数据在智能家居环境监测与智能调节中的应用拓展(423)

Java 大视界 -- Java 大数据在智能家居环境监测与智能调节中的应用拓展(423) * 引言: * 快速上手指南:3 步跑通智能家居 Demo(新手友好) * Step 1:环境准备(必装软件清单) * Step 2:代码运行(按顺序执行) * Step 3:效果验证(用 Postman 模拟数据) * 正文: * 一、智能家居环境监测与调节的核心痛点 * 1.1 设备数据的 “异构化” 困境 * 1.1.1 多源数据的 “协议壁垒” * 1.1.2 数据规模的 “爆发式增长” * 1.2 实时调节的 “滞后性” 痛点 * 1.

By Ne0inhk

爆肝 2 天,用 GLM5 开发了 OpenClaw 接入微信 bot,已开源!

这是苍何的第 493 篇原创! 大家好,我是苍何。 OpenClaw,这个 GitHub 上 18 万 Star 的怪物级开源项目,你们应该都听过了吧? 飞书能接、钉钉能接、企业微信能接、QQ 能接、Discord 能接…… 但偏偏最多人用的「微信个人号」,它不支持。 我翻遍了 GitHub、掘金、知乎,找到的方案要么是企业微信绕一圈,要么是用微信 Web 协议搞,动不动就封号。 说实话,这谁顶得住? 天天在微信上跟朋友聊天、在群里吹水,结果想接个 OpenClaw 都这么费劲? 麻了。 于是我决定自己干。 「爆肝 2 天,我把 OpenClaw 接入了微信个人号,并且已经开源了。」 地址:

By Ne0inhk
Git 用户名与邮箱配置指南

Git 用户名与邮箱配置指南

前言 在使用 Git 进行版本控制时,每一次代码提交(commit)都会记录提交者的身份信息。这些信息不仅用于追踪代码变更历史,还在团队协作、代码审查和开源贡献中发挥着重要作用。 Git 通过 用户名(user.name) 和 邮箱(user.email) 来标识开发者身份。正确配置这两项信息,是使用 Git 的第一步,也是确保提交记录清晰、可追溯的关键。 一、为什么需要设置用户名和邮箱? Git 是一个分布式版本控制系统,它不依赖中央服务器来管理用户身份。因此,每个开发者必须在本地明确声明自己的身份。Git 会在每次执行 git commit 时,自动将 user.name 和 user.email 写入提交记录。 如果没有正确设置,可能会导致: * 提交记录显示为 unknown 或默认系统用户名;

By Ne0inhk