Java 虚拟线程 pinned同步块为什么让虚拟线程失去并发优势怎样在批处理代码中减少额外分配
来源:17golang原创
时间:2026-11-10 04:35:42 200浏览 收藏
同步块导致虚拟线程占住载体的结论必须加上版本条件:JDK 21–23 中,持有 synchronized 监视器时阻塞可能出现 pinning;JDK 24 引入 JEP 491 后,普通监视器相关阻塞不再因这一原因固定载体。但共享锁造成的业务串行仍然存在。减少批处理分配则要从对象所有权入手,让缓冲区在一个批次内复用,而不是让所有虚拟线程共用可变对象。
官方变更地址:https://www.oracle.com/java/technologies/javase/24-relnote-issues.html。OpenJDK 提案地址:https://openjdk.org/jeps/491。下面讨论这一已落地的变化怎样影响现有代码,不把它当作刚发布的消息。
1. 先按 JDK 版本区分 pinning 与锁竞争
虚拟线程借用载体线程执行;监视器保护共享状态。旧版本中,虚拟线程在同步块内进行某些阻塞调用时,无法像普通可卸载等待那样释放载体。高并发下,有限载体被长时间占用,就会削弱虚拟线程适合等待密集任务的优势。同步块本身不是错误,关键是版本、持锁范围与阻塞时长。

| 情况 | 应当判断什么 |
|---|---|
| JDK 21–23 持监视器阻塞 | 可能固定载体,优先关注频繁的长等待 |
| JDK 24 普通 synchronized 阻塞 | JEP 491 已改变监视器相关固定行为 |
| 所有版本共用一把业务锁 | 锁内工作仍互斥,吞吐可能受串行段限制 |
| 主要时间用于 CPU 运算 | 虚拟线程不增加可用 CPU 算力 |
原生代码等其他限制不能由“synchronized 已改善”推导为全部消失。实际发行版有回移差异时,应以该构建说明为准。
2. 用短临界区隔离批处理的阻塞工作
如果锁只保护提交计数,就让编码、等待与提交分开:批次任务持有输入列表和私有缓冲,编码函数写入私有缓冲;提交锁只保护共享计数。以下示例可用于 Java 21 及以上环境,保存为 BatchPacking.java。本文没有执行该程序,也不提供虚构的性能数字。
import java.util.List;
public class BatchPacking {
private final Object commitLock = new Object();
private long committedChars;
public void pack(List rows) throws InterruptedException {
// 每个批次独占一个缓冲区,避免每行都新建构建器
StringBuilder buffer = new StringBuilder(4096);
for (String row : rows) {
buffer.setLength(0);
encode(row, buffer);
int chars = buffer.length();
// 模拟阻塞边界,实际项目替换为有期限的客户端调用
Thread.sleep(1);
// 仅提交共享状态,阻塞调用不持有提交锁
synchronized (commitLock) {
committedChars += chars;
}
}
}
private static void encode(String row, StringBuilder out) {
// 追加到已有对象;本例只演示缓冲所有权,不定义网络协议
out.append("row=").append(row);
}
public long committedChars() {
// 读取与更新使用相同监视器,保证可见性
synchronized (commitLock) {
return committedChars;
}
}
public static void main(String[] args) throws InterruptedException {
BatchPacking job = new BatchPacking();
Thread task = Thread.ofVirtual().start(() -> {
try {
job.pack(List.of("alpha", "beta", "gamma"));
} catch (InterruptedException e) {
// 保留中断信号,让调用方能够结束批次
Thread.currentThread().interrupt();
}
});
task.join();
System.out.println(job.committedChars());
}
} # 使用目标 JDK 编译、运行相同源文件,避免混用运行时 javac BatchPacking.java java BatchPacking
该程序按编码后的字符数累计,语义上总计为 26。Thread.sleep 仅模拟等待;它不是网络调用的性能替身,也不能据此计算线上提速倍数。若业务要求外部请求与状态更新具有原子性,不能直接把请求移出锁;需要版本号、事务或幂等提交等适合业务的并发协议。
3. 将缓冲区限定为批次私有对象
私有缓冲是每次 pack 调用创建一次的 StringBuilder;setLength(0) 清除逻辑内容以供下一行使用。这样减少的是逐行创建构建器的机会,未声称整个编码过程零分配。输入 String、列表、虚拟线程以及真实客户端内部仍可能产生对象。

不要把它改成静态共享构建器,否则多个线程会相互覆盖;给它加一把全局锁又会让编码串行。也不要为了复用大缓冲盲目迁移到 ThreadLocal:大量短生命周期虚拟线程可能各自持有一份大对象,失去平台线程池长期复用时的成本优势。
4096 是示例初始容量而非推荐阈值。缓冲扩容后,清空长度不会主动缩小容量;遇到异常大行,可以按业务上限重新创建缓冲或拒绝超限输入。若异步客户端保留传入对象,必须等待消费结束或传递独立不可变副本,不能立即清空复用。若需要字节数,应以明确字符集编码后的长度统计,字符数不能充当传输字节数。
4. 比较方案时同时观察等待与分配
对旧方案、短临界区方案和升级后的运行时使用相同数据量、等待分布、连接池限制和并发上限。分别观察完成吞吐、尾部耗时、锁竞争、载体占用、分配速率、GC 与峰值存活内存。JDK 21–23 可结合 JFR 的虚拟线程固定事件定位长期等待;JDK 24 的监视器实现已变化,不能因为旧固定事件减少,就认定锁竞争也减少。
分配优化和 pinning 优化应分别归因:前者涉及每行对象与缓冲保留,后者涉及阻塞期间的调度限制。升级 JDK 仍须兼顾依赖和运维兼容;改变锁实现也必须保持原有互斥、可见性与异常释放语义。
升级后还要全部替换 synchronized 吗?
不需要机械替换。先依据目标运行时和等待热点判断,短小的共享状态更新可以继续使用监视器。
复用 StringBuilder 是否必然更快?
不保证。小对象可能被编译器优化;大缓冲保留也可能增加内存成本。用实际负载比较分配与存活内存。
一个虚拟线程处理无限大的批次合适吗?
应按资源预算划分批次,控制输入保留、客户端并发与任务期限。轻量线程不代表数据库连接或下游服务也无限可用。
虚拟线程原始设计及线程本地资源说明:https://openjdk.org/jeps/444;Oracle 核心库指南:https://docs.oracle.com/en/java/javase/22/core/java-core-libraries-developer-guide.pdf。
-
501 收藏
-
文章 · java教程 | 2个月前 | Java · 异常处理 · 资源管理 · java try-with-resources AutoCloseable close suppressed exception501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
199 收藏
-
495 收藏
-
420 收藏
-
368 收藏
-
339 收藏
-
298 收藏
-
320 收藏
-
318 收藏
-
427 收藏
-
391 收藏
-
185 收藏
-
346 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习