乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • 学习路径
  • 第1章 Java概述与环境搭建

    • Java概述与环境搭建
    • Java语言概述
    • 解释型语言与编译型语言对比
    • JDK安装与配置
    • JDK、JRE、JVM 详解
    • HelloWorld程序详解
    • IDE 介绍
  • 第2章 标识符与基本数据类型

    • 章节导读
    • 变量概述
    • 常量概述
    • 基本类型与包装类
    • 字节型 byte
    • 短整型 short
    • 整型 int
    • 长整型 long
    • 单精度浮点型 float
    • 双精度浮点型 double
    • 字符型 char
    • 布尔型 boolean
    • 类型转换
  • 第3章 运算符与表达式

    • 章节导读
    • 算术运算符
    • 赋值运算符
    • 关系运算符
    • 逻辑运算符
    • 位运算符
    • 条件运算符
    • 运算符优先级
    • 表达式
  • 第4章 流程控制

    • 章节导读
    • 常见的程序运行流程
    • if-else 选择结构
    • switch 多分支选择
    • while 循环
    • do-while 循环
    • for 循环
    • break 与 continue
  • 第5章 数组

    • 章节导读
    • 一维数组
    • 多维数组
    • Arrays 工具类
  • 第6章 类与对象

    • 章节导读
    • 类与对象
    • 方法定义与调用
    • 构造方法
    • 封装
    • 访问修饰符
    • package 与 import
    • static 关键字
    • this 关键字
    • 参数传递 详解
    • 枚举
    • 成员内部类
    • 局部内部类
    • 静态内部类
    • 匿名内部类
  • 第7章 接口与继承

    • 章节导读
    • 继承
    • super 关键字
    • final 关键字
    • 多态
    • 向上转型与向下转型
    • 抽象类
    • 接口
    • 抽象类与接口对比
  • 第8章 注解

    • 章节导读
    • 注解基础
    • 元注解详解
    • 自定义注解
  • 第9章 常用类

    • 章节导读:Java 常用类
    • Object 类:万类之祖
    • 包装类:基本类型的对象化
    • String:不可变的字符串
    • StringBuffer:线程安全的可变字符串
    • StringBuilder:可变的字符串构建器
    • Math:数学运算工具类
    • Random:伪随机数生成器
    • 大数值运算 详解
    • 日期时间API 详解
  • 第10章 异常机制

    • 章节导读
    • 异常体系与分类
    • try-catch-finally
    • try-with-resources
    • throws 与 throw
    • 自定义异常
  • 第11章 泛型

    • 章节导读
    • 泛型基础
    • 通配符与PECS原则
    • 类型擦除
  • 第12章 集合框架

    • 章节导读
    • 集合框架概述
    • ArrayList
    • LinkedList
    • HashMap 详解
    • LinkedHashMap 详解
    • TreeMap 详解
    • HashSet
    • TreeSet 详解
    • TreeSet 与 Comparable
    • Collections 工具类详解
  • 第13章 IO流

    • 章节导读
    • IO流概述
    • 字节流
    • 字符流
    • 缓冲流
    • 转换流 详解
    • 序列化 详解
    • NIO与Files 详解
    • NIO与Files工具类
  • 第14章 多线程与并发

    • 第十六章 多线程与并发 —— 章节导读
    • 线程基础详解
    • synchronized 详解
    • Lock 与显式锁详解
    • volatile 详解
    • wait 与 notify 详解
    • ThreadLocal详解
    • 原子类详解
    • 并发工具类详解
    • 线程池详解
  • 第15章 反射

    • 章节导读
    • 反射概述与 Class 对象
    • Constructor 与对象创建
    • Field与Method详解
    • 反射应用详解
  • 第16章 JDK8新特性

    • 章节导读
    • Lambda 表达式
    • Stream API 基础
    • Stream API 高级详解
    • Optional 详解
    • 新日期时间API详解
  • 第17章 JDK9-11新特性

    • 章节导读
    • 模块化系统 — Project Jigsaw(JDK 9)
    • var 局部变量类型推断(JDK 10)
    • 集合工厂方法与增强(JDK 9 / 10 / 11)
    • 接口增强:private 方法(JDK 9)
    • Stream API 增强(JDK 9)
    • Optional 增强(JDK 9 / 10 / 11)
    • String 新增方法(JDK 11)
    • HTTP Client 与 Files 增强(JDK 11)
    • 直接运行 Java 源文件 — JEP 330(JDK 11)
  • 第18章 JDK12-17新特性

    • 章节导读
    • Switch 表达式(JDK 12 预览 / JDK 14 正式)
    • 文本块 Text Blocks(JDK 13 预览 / JDK 15 正式)
    • Records 记录类(JDK 14 预览 / JDK 16 正式)
    • 密封类 Sealed Classes(JDK 15 预览 / JDK 17 正式)
    • instanceof 模式匹配(JDK 14 预览 / JDK 16 正式)
    • Switch 模式匹配 — Pattern Matching for switch(JDK 17 预览 / JDK 21 正式)
    • Helpful NPE 与 String 增强(JDK 12 / JDK 14 / JDK 15)
    • Stream 增强(JDK 12 / JDK 16)
    • 日期时间增强 — Day Period 支持(JDK 16)
  • 第19章 JDK18-21新特性

    • 章节导读
    • 虚拟线程(JDK 19 预览 / JDK 20 第二预览 / JDK 21 正式)
    • 序列集合(JDK 21 正式)
    • Switch 模式匹配(JDK 17 预览 / JDK 18 第二预览 / JDK 20 第四预览 / JDK 21 正式)
    • Record 模式匹配(JDK 19 预览 / JDK 20 第二预览 / JDK 21 正式)
    • 未命名模式与变量(JDK 21 预览 / JDK 22 正式)
  • 第20章 JDK 22-25 新特性

    • 章节导读
    • 字符串模板(JDK 22 预览 / JDK 23 第二预览 / JDK 24 第三预览)
    • Stream Gatherers(JDK 22 预览 / JDK 24 第二预览)
    • 隐式声明类与实例方法(JDK 23 预览 / JDK 24 第二预览)
    • 原始类型模式匹配(JDK 24 预览)
  • 附录

    • Java 核心知识点
    • Java SE 专业术语
    • Java特性索引(JDK 8 → 25)

大翔(CTO):"小崔,咱们的网关服务又扛不住了,线程池打满,响应时间飙到 5 秒!" 小崔(后端开发):"大翔哥,我已经把线程池调到 500 了,再调下去上下文切换开销更大,而且每个平台线程占 1MB 栈内存,机器内存快撑爆了……" 白歌(架构师):"问题的根源在于 —— 我们用有限的平台线程去处理大量等待 I/O 的任务。JDK 21 的虚拟线程就是来解决这个问题的,它让 '一个请求一个线程' 的编程模型重新变得可行。" 孔蓝(后端新人):"虚拟线程?是像 Go 的 goroutine 那样的东西吗?" 白歌:"没错!虚拟线程由 JVM 调度,内存开销只有几 KB,可以轻松创建上百万个。今天我们就来彻底搞懂它。"

虚拟线程(JDK 19 预览 / JDK 20 第二预览 / JDK 21 正式)

什么是虚拟线程

概念描述
虚拟线程(Virtual Thread)由 JVM 管理的轻量级线程,不直接绑定操作系统线程,调度在 Java 运行时完成
平台线程(Platform Thread)传统的 Java 线程,是操作系统线程的直接包装,由 OS 调度,创建和切换成本高
载体线程(Carrier Thread)执行虚拟线程的平台线程,虚拟线程在阻塞时会从载体线程上"卸载",让载体线程去执行其他虚拟线程
卸载(Unmount)虚拟线程遇到阻塞操作(如 I/O)时,暂时从载体线程上分离,载体线程可继续执行其他虚拟线程
钉住(Pin)虚拟线程因某些原因无法卸载,被迫继续占用载体线程,失去了虚拟线程的核心优势

虚拟线程 vs 平台线程:本质区别

对比维度平台线程虚拟线程
内存占用默认 ~1 MB(栈空间)初始仅 ~200-300 bytes,栈在 Java 堆上
调度者操作系统内核JVM(协作调度)
创建成本高(系统调用)极低(纯 Java 操作)
上下文切换内核态切换,昂贵JVM 用户态切换,廉价
适用数量级数百到数千数百万
阻塞代价占用整个 OS 线程自动卸载,释放载体线程

核心机制:卸载(Unmount)与挂载(Mount)

关键理解:虚拟线程的阻塞发生在 Java 运行时层面,不会阻塞底层的操作系统线程。JVM 在网络 I/O、文件系统 I/O、LockSupport.park() 等场景中会自动触发卸载。

基础用法

方式一:Thread.startVirtualThread()

// === 场景说明:最简洁的虚拟线程创建方式,适合一次性任务 ===

// 创建并启动一个虚拟线程
Thread vThread = Thread.startVirtualThread(() -> {
    System.out.println("当前线程: " + Thread.currentThread()); // 行内注释:线程名以 "VirtualThread" 开头
    System.out.println("是否为虚拟线程: " + Thread.currentThread().isVirtual()); // 行内注释:返回 true
    try {
        Thread.sleep(1000); // 行内注释:sleep 会触发卸载,不占用载体线程
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
    System.out.println("虚拟线程执行完毕");
});

// 等待虚拟线程结束
vThread.join();
当前线程: VirtualThread[#21]/runnable@ForkJoinPool-1-worker-1
是否为虚拟线程: true
虚拟线程执行完毕

方式二:Thread.ofVirtual() 工厂方法

// === 场景说明:使用 Thread.Builder 工厂,支持更多配置(名称、异常处理器等) ===

// 创建带名称的虚拟线程
Thread vThread = Thread.ofVirtual()
        .name("order-processing-", 0) // 行内注释:名称前缀 + 自增序号
        .uncaughtExceptionHandler((t, e) -> 
            System.err.println("线程 " + t + " 抛出异常: " + e))
        .start(() -> {
            System.out.println("线程名称: " + Thread.currentThread().getName());
            System.out.println("虚拟线程? " + Thread.currentThread().isVirtual());
        });

vThread.join();
线程名称: order-processing-0
虚拟线程? true

方式三:Executors.newVirtualThreadPerTaskExecutor()

// === 场景说明:为每个任务创建一个虚拟线程,无需配置线程池大小,适合替代固定大小线程池 ===

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { // 行内注释:实现 AutoCloseable,用完需关闭
    // 提交 5 个任务,每个任务运行在独立的虚拟线程中
    for (int i = 0; i < 5; i++) {
        int taskId = i;
        executor.submit(() -> {
            System.out.println("任务 " + taskId + " 运行在: " + Thread.currentThread());
            try {
                Thread.sleep(500); // 模拟 I/O 等待
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            return taskId;
        });
    }
} // 行内注释:try-with-resources 自动关闭,等待所有任务完成
任务 0 运行在: VirtualThread[#22]/runnable@ForkJoinPool-1-worker-1
任务 1 运行在: VirtualThread[#23]/runnable@ForkJoinPool-1-worker-2
任务 2 运行在: VirtualThread[#24]/runnable@ForkJoinPool-1-worker-3
任务 3 运行在: VirtualThread[#25]/runnable@ForkJoinPool-1-worker-1
任务 4 运行在: VirtualThread[#26]/runnable@ForkJoinPool-1-worker-2

钉住(Pin)问题

虚拟线程在以下场景中会被"钉住",无法卸载,导致载体线程被占用:

钉住原因说明解决方案
synchronized 同步块/方法虚拟线程在 synchronized 内遇到阻塞时无法卸载改用 ReentrantLock(支持虚拟线程卸载)
Object.wait()调用 wait() 时会钉住载体线程改用 Condition.await() 或 LockSupport.park()
JNI 调用进入本地代码时无法卸载避免在高并发路径中使用 JNI

压测对比:1 万个线程

// === 场景说明:对比创建 10000 个平台线程 vs 10000 个虚拟线程的资源消耗 ===
// 运行前请用 -Xmx 调整堆内存,平台线程版本需要较大内存

import java.util.concurrent.*;

public class ThreadStressTest {

    // ❌ 平台线程版本:创建 10000 个平台线程,极易 OOM 或耗时极长
    static void platformThreadTest() throws InterruptedException {
        long start = System.currentTimeMillis();
        int count = 10000;
        Thread[] threads = new Thread[count];
        for (int i = 0; i < count; i++) {
            threads[i] = new Thread(() -> {
                try {
                    Thread.sleep(1000); // 模拟 I/O 等待
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
            threads[i].start(); // 行内注释:每个 start() 都向 OS 申请线程,成本高
        }
        for (Thread t : threads) {
            t.join();
        }
        System.out.println("平台线程耗时: " + (System.currentTimeMillis() - start) + "ms");
    }

    // ✅ 虚拟线程版本:创建 10000 个虚拟线程,内存占用极低
    static void virtualThreadTest() throws InterruptedException {
        long start = System.currentTimeMillis();
        int count = 10000;
        Thread[] threads = new Thread[count];
        for (int i = 0; i < count; i++) {
            threads[i] = Thread.startVirtualThread(() -> {
                try {
                    Thread.sleep(1000); // 行内注释:sleep 触发卸载,不占用载体线程
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
            // 行内注释:创建虚拟线程是纯 Java 操作,极快
        }
        for (Thread t : threads) {
            t.join();
        }
        System.out.println("虚拟线程耗时: " + (System.currentTimeMillis() - start) + "ms");
    }

    public static void main(String[] args) throws Exception {
        System.out.println("=== 虚拟线程压测对比 ===");
        System.out.println("可用处理器: " + Runtime.getRuntime().availableProcessors());
        virtualThreadTest(); // 推荐先运行虚拟线程版本
        // platformThreadTest(); // 谨慎运行!可能需要调大内存
    }
}
=== 虚拟线程压测对比 ===
可用处理器: 8
虚拟线程耗时: 1256ms

(平台线程版本在默认 JVM 配置下通常会抛出:
 java.lang.OutOfMemoryError: unable to create native thread
)

实战场景:飞翔科技网关层改造

// === 场景说明:模拟网关层改造,对比线程池模型与虚拟线程模型的吞吐量 ===

import java.util.concurrent.*;
import java.util.stream.IntStream;

public class GatewayRefactorDemo {

    // 模拟 I/O 操作(如数据库查询、HTTP 调用)
    static void simulateIO(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    // 改造前:固定大小平台线程池
    static void oldStyleGateway(int requestCount) throws Exception {
        long start = System.currentTimeMillis();
        try (var executor = Executors.newFixedThreadPool(200)) { // 行内注释:最多 200 并发
            var futures = IntStream.range(0, requestCount)
                .mapToObj(i -> executor.submit(() -> simulateIO(100)))
                .toList();
            for (var f : futures) f.get();
        }
        System.out.println("改造前(平台线程池200) 处理 " + requestCount + " 请求耗时: "
                + (System.currentTimeMillis() - start) + "ms");
    }

    // 改造后:虚拟线程执行器
    static void newStyleGateway(int requestCount) throws Exception {
        long start = System.currentTimeMillis();
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { // 行内注释:无上限,每个任务一个虚拟线程
            var futures = IntStream.range(0, requestCount)
                .mapToObj(i -> executor.submit(() -> simulateIO(100)))
                .toList();
            for (var f : futures) f.get();
        }
        System.out.println("改造后(虚拟线程) 处理 " + requestCount + " 请求耗时: "
                + (System.currentTimeMillis() - start) + "ms");
    }

    public static void main(String[] args) throws Exception {
        int requests = 5000;
        System.out.println("=== 飞翔科技网关压测(每个请求模拟 100ms I/O)===");
        newStyleGateway(requests); // 先跑虚拟线程版本
        // oldStyleGateway(requests); // 平台线程池版(200 并发处理 5000 请求,至少需要 2500ms 以上)
    }
}
=== 飞翔科技网关压测(每个请求模拟 100ms I/O)===
改造后(虚拟线程) 处理 5000 请求耗时: 1347ms

(改造前使用 200 线程池处理 5000 个 100ms I/O 的请求,
 理论最短时间 = 5000 / 200 * 100ms = 2500ms,
 实际因上下文切换会更长)

适用场景与不适用场景

类型场景原因
✅ 适用高并发 I/O 密集型服务虚拟线程在等待 I/O 时自动卸载,载体线程利用率高
✅ 适用每个请求一个线程的 Web 框架编程模型简单,无需手动管理异步回调
❌ 不适用计算密集型任务虚拟线程不增加 CPU 并行度,纯计算不会触发卸载
❌ 不适用长时间持有 synchronized 锁钉住载体线程,丧失虚拟线程优势
❌ 不适用大量 JNI 调用JNI 调用期间无法卸载

易错场景

错误一:在虚拟线程中使用 synchronized

// ❌ 错误:在 synchronized 块内做 I/O,会钉住载体线程
public class PinnedExample {
    private static final Object LOCK = new Object();

    public static void wrongWay() {
        for (int i = 0; i < 100; i++) {
            Thread.startVirtualThread(() -> {
                synchronized (LOCK) { // 行内注释:钉住!虚拟线程在 synchronized 内阻塞时无法卸载
                    System.out.println("线程: " + Thread.currentThread().getName());
                    try {
                        Thread.sleep(1000); // I/O 等待,但载体线程被钉住!
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }
            });
        }
    }

    // ✅ 正确:改用 ReentrantLock
    private static final ReentrantLock LOCK2 = new ReentrantLock();

    public static void correctWay() {
        for (int i = 0; i < 100; i++) {
            Thread.startVirtualThread(() -> {
                LOCK2.lock(); // 行内注释:获取锁,但等待锁时仍可卸载(公平锁除外)
                try {
                    System.out.println("线程: " + Thread.currentThread().getName());
                    try {
                        Thread.sleep(1000); // 行内注释:✅ 可卸载!载体线程被释放
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                } finally {
                    LOCK2.unlock();
                }
            });
        }
    }
}

错误二:用虚拟线程跑 CPU 密集型任务

// ❌ 错误:虚拟线程不适合纯计算任务
public class CpuIntensiveWrong {
    public static void main(String[] args") {
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            for (int i = 0; i < 10000; i++) {
                executor.submit(() -> {
                    // 行内注释:纯计算,永不触发卸载,虚拟线程数量远超 CPU 核心数,反而增加调度开销
                    long sum = 0;
                    for (long j = 0; j < 1_000_000_0; j++) {
                        sum += j;
                    }
                    return sum;
                });
            }
        }
    }
}

// ✅ 正确:CPU 密集型任务使用平台线程池,线程数 = CPU 核心数
public class CpuIntensiveCorrect {
    public static void main(String[] args") {
        int cores = Runtime.getRuntime().availableProcessors();
        try (var executor = Executors.newFixedThreadPool(cores)) { // 行内注释:计算任务线程数 = CPU 核心数
            for (int i = 0; i < 100; i++) {
                executor.submit(() -> {
                    long sum = 0;
                    for (long j = 0; j < 1_000_000_0; j++) {
                        sum += j;
                    }
                    return sum;
                });
            }
        }
    }
}

错误三:误以为虚拟线程能突破 CPU 上限

// ❌ 错误认知:创建 1 万个虚拟线程 = 1 万个并发计算
// 实际情况:虚拟线程只是调度单位,底层载体线程(ForkJoinPool)大小 = CPU 核心数
// 对于计算任务,1 万个虚拟线程反而比 8 个平台线程更慢(调度开销)

// ✅ 正确理解:
// - 虚拟线程提升的是 "等待 I/O 时的吞吐量",不是 CPU 计算速度
// - 计算任务并发度上限仍然是 CPU 核心数

面试考点

问题 1:虚拟线程和平台线程的根本区别是什么?虚拟线程是如何实现"一个请求一个线程"而不耗尽系统资源的?

虚拟线程与平台线程的根本区别在于调度层级和内存模型。平台线程是 OS 线程的一对一映射,由操作系统调度,每个线程占用约 1MB 栈内存;虚拟线程由 JVM 调度,栈帧存储在 Java 堆上,初始内存开销仅几百字节。虚拟线程通过"卸载"机制实现高并发:当虚拟线程执行阻塞 I/O 时,JVM 将其从载体线程(一个普通的平台线程)上卸载,载体线程可继续执行其他虚拟线程。这样,少量载体线程(默认等于 CPU 核心数)就能支撑海量虚拟线程并发,使得"一个请求一个线程"的编程模型在高并发场景下重新变得可行。

问题 2:什么是虚拟线程的"钉住"(Pin)问题?哪些场景会导致钉住?如何避免?

钉住是指虚拟线程在无法卸载的情况下占有载体线程,导致载体线程在虚拟线程阻塞期间无法执行其他虚拟线程。主要钉住场景包括:1)在 synchronized 同步块/方法内遇到阻塞操作;2)调用 Object.wait();3)执行 JNI 本地方法。避免方案:将 synchronized 替换为 ReentrantLock(ReentrantLock 的 lock() 在等待时也会钉住,但 Condition.await() 不会);避免在虚拟线程中长时间持有任何锁;减少 JNI 调用。需要注意的是,JDK 21 中部分 synchronized 钉住问题已在后续版本中通过 JEP 453(Structured Concurrency)等改进,但最佳实践仍是优先使用 java.util.concurrent.locks 包中的锁。

问题 3:Executors.newVirtualThreadPerTaskExecutor() 没有线程池大小限制,会不会导致资源耗尽?如何正确使用?

虽然 newVirtualThreadPerTaskExecutor() 不限制虚拟线程数量,但并不意味着没有资源上限。限制来自于:1)外部资源,如数据库连接池大小、HTTP 连接池大小、下游服务吞吐能力;2)内存,虽然单个虚拟线程内存小,但海量虚拟线程的局部变量和调用栈累积仍可能 OOM;3)载体线程的 ForkJoinPool 饱和(但 I/O 型任务不会饱和)。正确使用方式:在虚拟线程之上,对外部资源访问处进行限流(如使用 Semaphore 限制并发数据库查询数),而不是直接限制虚拟线程数量。虚拟线程的设计哲学是"不限制数量,而是限制它们访问的共享资源"。

知识图谱


白歌(架构师):"虚拟线程不是银弹,它解决的是 '等待' 的成本,而不是 '计算' 的速度。理解卸载和钉住,是用好虚拟线程的关键。" 小崔(后端开发):"明白了!我们网关的瓶颈在 I/O 等待,换成虚拟线程后,同样的硬件配置能撑住 10 倍的并发量。" 孔蓝(后端新人):"那我以后是不是可以随便 newVirtualThreadPerTaskExecutor 了?" 大翔(CTO):"可以,但记得 —— 虚拟线程免费,数据库连接不免费。限流要在资源层做,不要在线程层做。去写代码吧!"

上一页
章节导读
下一页
序列集合(JDK 21 正式)