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

Flutter 组件 ews 的适配 鸿蒙Harmony 实战 - 驾驭企业级 Exchange Web Services 协议、实现鸿蒙端政企办公同步与高安通讯隔离方案

Flutter 组件 ews 的适配 鸿蒙Harmony 实战 - 驾驭企业级 Exchange Web Services 协议、实现鸿蒙端政企办公同步与高安通讯隔离方案

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 ews 的适配 鸿蒙Harmony 实战 - 驾驭企业级 Exchange Web Services 协议、实现鸿蒙端政企办公同步与高安通讯隔离方案 前言 在鸿蒙(OpenHarmony)生态进军政企办公领域的过程中,与现有企业信息化基础设施的深度集成是一道必答题。即便是在全连接、分布式的今天,微软的 Exchange 服务器依然是全球无数大厂与政务系统处理邮件、日历同步的核心底座。 对于习惯了简单 http.get 的移动开发者来说,Exchange Web Services(EWS)协议由于其复杂的 SOAP 封装、繁琐的 XML 数据结构以及极其严苛的身份认证机制,往往是一块难啃的“骨头”。 ews 库为 Dart 提供了成熟的、类型安全的

By Ne0inhk
Flutter for OpenHarmony: Flutter 三方库 fixnum 解决鸿蒙 Web 与原生端 64 位大整数精度失真难题(精准计算护卫)

Flutter for OpenHarmony: Flutter 三方库 fixnum 解决鸿蒙 Web 与原生端 64 位大整数精度失真难题(精准计算护卫)

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 在进行 OpenHarmony 的跨平台开发时,你可能会遇到一个诡异的 Bug:同样的 64 位长整数(如 Int64),在鸿蒙原生(Native)模式下运行正常,但编译为 Flutter Web 模式在浏览器运行时,数值却发生了精度漂移或溢出。 1. 产生原因:JavaScript 原生的数字类型实质上是 64 位浮点数,它能安全表示的最大整数只有 53 位( 2 53 − 1 2^{53}-1 253−1)。 2. 后果:大额订单 ID、高精度的金融分位值、或是底层硬件的 64 位地址位,在

By Ne0inhk
Flutter for OpenHarmony:web_socket_channel 全平台 WebSocket 通信标准库,从原理到鸿蒙实战(3000字深度解析)

Flutter for OpenHarmony:web_socket_channel 全平台 WebSocket 通信标准库,从原理到鸿蒙实战(3000字深度解析)

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 在现代 App 开发中,实时通信(Real-time Communication)已成为标配。无论是社交聊天的由“推”变“拉”,还是股票行情的毫秒级跳动,亦或是智能家居的状态同步,传统的 HTTP 轮询(Polling)已无法满足低延迟、高并发的需求。 WebSocket 协议应运而生。它基于 TCP,但在握手阶段利用 HTTP 升级协议(Upgrade Header),成功后建立全双工(Full-Duplex)的长连接。在这条通道上,客户端和服务端可以随时互相推送数据,且头部开销极小。 在 Flutter 生态中,虽然 dart:io 提供了原生的 WebSocket 类,dart:

By Ne0inhk
【面试分享】前端 React 50个基础高频面试题,助你轻松拿 offer!

【面试分享】前端 React 50个基础高频面试题,助你轻松拿 offer!

目录 前端基础高频面试题之-- React 篇 1、什么是React? 2、React有什么特点? 3、列出React的一些主要优点。 4、React有哪些限制? 5、什么是JSX? 6、为什么浏览器无法读取JSX? 7、React中的组件是什么? 8、怎样解释 React 中 render() 的目的。 9、什么是 Props? 10、React中的状态是什么?它是如何使用的? 11、 React 中的箭头函数是什么?使用箭头函数的好处? 12、什么是高阶组件(HOC)? 13、你能用HOC做什么? 14、什么是纯组件? 16、什么是React 路由? 17、为什么 useState 返回的是数组而不是对象? 18、如何实现

By Ne0inhk