目录

1.虚拟线程
虚拟线程是一种由JVM而非操作系统管理的线程。独立于操作系统线程(也称为平台线程),具有以下核心特性:
-
轻量级:每个虚拟线程仅需几百字节内存,与传统线程(2MB~20MB)相比,资源消耗降低99%以上。
-
高并发:支持数百万级别的并发线程,不会因线程数量限制系统性能。
-
与传统线程兼容:完全兼容现有的线程API,无需修改代码即可引入虚拟线程
性能对比
// 传统线程池(平台线程)
ExecutorService executor = Executors.newFixedThreadPool(200);
// 虚拟线程池
ExecutorService virtualExecutor = Executors.newVirtualThreadPerTaskExecutor();
// 执行10万请求
for (inti=0; i < 100_000; i++) {
executor.submit(() -> handleRequest()); // 平台线程
virtualExecutor.submit(() -> handleRequest()); // 虚拟线程
}
| 指标 | 平台线程池(200线程) | 虚拟线程池 |
|---|---|---|
| 内存占用 | 4.2GB | 800MB |
| 请求完成时间 | 32秒 | 6秒 |
为啥虚拟线程这么牛
传统的线程模型中,每个线程都映射到操作系统线程,但大部分时间都在等IO,造成资源浪费。而虚拟线程采用挂起-恢复机制,在等待IO时会把自己挂起,释放底层的载体线程(Carrier Thread)去干别的活。
创建方式
虚拟线程的创建方式与传统线程类似,通过Thread.ofVirtual()或Executors.newVirtualThreadPerTaskExecutor()实现。
-
直接创建虚拟线程
public class VirtualThreadExample {
public static void main(String[] args) throws InterruptedException {
// 创建虚拟线程
Thread virtualThread = Thread.ofVirtual().start(() -> {
System.out.println("虚拟线程执行任务: " + Thread.currentThread().getName());
});
virtualThread.join(); // 等待虚拟线程执行完成
}
}
//结果
//虚拟线程执行任务: VirtualThread-1
-
使用虚拟线程池:每个任务对应一个虚拟线程,系统资源占用极低。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class VirtualThreadPoolExample {
public static void main(String[] args) {
// 创建虚拟线程池
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
// 提交大量任务
for (int i = 1; i <= 1000; i++) {
int taskId = i;
executor.submit(() -> {
System.out.println("执行任务 " + taskId + ",线程名: " + Thread.currentThread().getName());
});
}
executor.shutdown(); // 关闭线程池
}
}
//结果
执行任务 1,线程名: VirtualThread-1
执行任务 2,线程名: VirtualThread-2
执行任务 3,线程名: VirtualThread-3
...
-
使用
try-with-resources管理线程池
虚拟线程池支持try-with-resources语法,方便资源管理。
import java.util.concurrent.Executors;
public class VirtualThreadPoolWithResources {
public static void main(String[] args) {
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 1; i <= 10; i++) {
int taskId = i;
executor.submit(() -> {
System.out.println("任务 " + taskId + " 由线程: " + Thread.currentThread().getName() + " 执行");
});
}
} // 线程池自动关闭
}
}
性能优化
1. IO密集型任务
某日志分析系统实测:处理百万条日志解析任务,虚拟线程池相比固定线程池吞吐量提升8倍。
//虚拟线程在HTTP请求、数据库操作等场景表现卓越
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 1_000_000; i++) {
executor.submit(() -> {
// 模拟HTTP调用
String response = httpClient.send(request);
//模拟数据库写入
saveToDB(response);
});
}
}
2. 异步任务编排
-
在复杂的业务场景中,任务通常需要多个步骤完成,例如从缓存查询数据、对数据进行加工、最后将结果返回给客户端。
-
为了提高效率,这些步骤可以异步执行,并且通过 链式调用+虚拟线程 实现流程编排。
CompletableFuture.supplyAsync(() -> queryFromCache(), virtualExecutor) //异步执行queryFromCache()方法,获取缓存数据 .thenApplyAsync(data -> enrichData(data), virtualExecutor) //对上一步的结果进行加工 .thenAcceptAsync(result -> sendResponse(result), virtualExecutor); //将最终结果发送给客户端
3. 高并发事件处理
-
传统方式中,每个连接都需要一个线程来处理,线程数量受限于操作系统的能力,难以支撑大规模并发。
-
为每个客户端连接启动一个虚拟线程,虚拟线程的开销极低,因此可以轻松支持数十万甚至上百万的并发连接。
ServerSocketChannel serverChannel = ServerSocketChannel.open().bind(new InetSocketAddress(8080));
while (true) {
SocketChannel clientChannel = serverChannel.accept(); //接受客户端连接
//为每个客户端连接启动一个虚拟线程,执行handleClient方法
Thread.startVirtualThread(() -> handleClient(clientChannel));
}
单机可轻松支撑10万+并发连接。
应用场景
改造Web服务提升吞吐量
现在有个REST API服务,主要提供数据查询,每个请求都需要查询数据库、调用远程服务,典型的IO密集型应用。用传统Tomcat+线程池方式,撑死了每秒处理1000个请求。
修改Spring Boot配置,启用虚拟线程:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
return protocolHandler -> {
protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
};
}
}
利用Java异步编程模型进一步优化(比如CompletableFuture)
@GetMapping(“/users/{id}/details”)
public UserDetailsDTO getUserDetails(@PathVariable Long id) {
// 使用虚拟线程执行器
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
CompletableFuture<UserDTO> userFuture = CompletableFuture.supplyAsync(
() -> userService.getUser(id), executor);
CompletableFuture<List<OrderDTO>> ordersFuture = CompletableFuture.supplyAsync(
() -> orderService.getUserOrders(id), executor);
// 并行执行两个任务,然后组合结果
return CompletableFuture.allOf(userFuture, ordersFuture)
.thenApply(v -> new UserDetailsDTO(userFuture.join(), ordersFuture.join()))
.join();
}
}
压测工具用的是Apache JMeter,配置了1000个并发用户,持续压测5分钟。结果简直惊呆了:
-
平台线程版本:最大吞吐量约1,200 RPS,平均响应时间800ms
-
虚拟线程版本:最大吞吐量约48,000 RPS,平均响应时间20ms
吞吐量提升40倍,响应时间降低40倍!这还是在相同硬件配置下(8核16G内存)
温馨提示:不要盲目用虚拟线程替换所有线程池,要根据应用特性决定。有些场景下传统线程池仍然有优势,比如需要控制并发度的场景。
注意事项
1. 阻塞操作优化
虚拟线程在synchronized块或本地方法调用时会固定(Pin)到平台线程,导致调度失效。解决方案:
-
使用
ReentrantLock替代synchronized -
避免在虚拟线程中调用JNI代码
// 错误示范
synchronized(lock) { database.query(); }
// 正确方案
database.query(); // 虚拟线程自动挂起
synchronized(lock) { updateSharedState(); } // 同步块控制在10ms内
2. 线程本地变量陷阱
ThreadLocal在虚拟线程中可能引发内存泄漏(线程生命周期长且数量多):
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> {
ThreadLocalRandom.current(); // 安全
MyContext.set(user); // 危险!需改用Scoped Values
});
}
Java 21引入Scoped Values(作用域值)替代ThreadLocal。
3. 资源耗尽防护
无限创建虚拟线程可能导致GC压力:
// 错误示例:无限制提交任务
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
for (inti=0; i < Integer.MAX_VALUE; i++) {
executor.submit(() -> heavyTask()); // 可能触发OOM
}
// 正确方案:限流队列
ExecutorService safeExecutor = new ThreadPoolExecutor(
1000, 1000, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10_000),
Thread.ofVirtual().factory()
);
4.不要池化虚拟线程
虚拟线程就是用完即丢的,不需要复用。
// 错误用法 ExecutorService badExecutor = Executors.newFixedThreadPool(100, Thread.ofVirtual().factory()); // 不要这样做! // 正确用法 ExecutorService goodExecutor = Executors.newVirtualThreadPerTaskExecutor();
2.模式匹配增强
Java 17 引入了模式匹配,JDK 21 在此基础上改进了对 instanceof 的模式匹配支持,可以直接在 instanceof 表达式中使用类型转换。
class Example {
void process(Object obj) {
if (obj instanceof String s) {
// 可以直接使用s,无需额外的类型转换
System.out.println("String length: " + s.length());
} else {
System.out.println("Not a String");
}
}
}
3.随机生成数改进
JDK 21 对随机生成器进行了改进,引入了一些新的方法和算法,提高了其性能和质量
import java.util.Random;
public class Example {
public static void main(String[] args) {
Random random = new Random();
int randomNumber = random.nextInt(100);
System.out.println("Random number: " + randomNumber);
}
}
4.非侵入式日志
JDK 21 提供了一种非侵入式的方式来进行 Java 日志记录,使得开发者能够更轻松地管理应用程序的日志信息。
import java.logging.Logger;
public class Example {
//使用Logger 类来记录日志信息,无需引入额外日志框架
private static final Logger logger = Logger.getLogger(Example.class.getName());
public static void main(String[] args) {
logger.info("This is an informational message.");
}
}
5.HTTP/2 客户端
JDK 11 引入了原生的 HTTP 客户端,JDK 21 进一步增强了对 HTTP/2 的支持,使得开发者能够更高效地与支持 HTTP/2 协议的服务器进行通信。
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Example {
public static void main(String[] args) throws Exception {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://example.com"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("Response code: " + response.statusCode());
System.out.println("Response body: " + response.body());
}
}
6.泛型增强
新的泛型特性包括泛型推断、泛型枚举和泛型实例方法。
// 泛型推断示例
var list = new ArrayList<String>(); // 可以省略类型参数
// 泛型枚举示例
enum Option<T> {
SOME, NONE;
}
// 泛型实例方法示例
class Utilities {
public <T> T getFirst(List<T> list) {
return list.get(0);
}
}
7.并发随机数生成器
该功能提供了一种并发安全的随机数生成器,使得开发者能够更安全地在多线程环境中生成随机数。
import java.util.concurrent.ThreadLocalRandom;
public class Example {
public static void main(String[] args) {
// 生成一个介于 0 和 100 之间的随机数
int randomNumber = ThreadLocalRandom.current().nextInt(0, 101);
System.out.println("Random number: " + randomNumber);
}
}
8.向量 API
该功能为 Java 增加了向量 API,使得开发者能够更高效地进行向量化操作,从而提升代码的性能。
import jdk.incubator.vector.*;
public class Example {
public static void main(String[] args) {
VectorSpecies<Float> species = FloatVector.SPECIES_256;
float[] a = new float[species.length()];
float[] b = new float[species.length()];
// 初始化数组
for (int i = 0; i < species.length(); i++) {
a[i] = i;
b[i] = i * 2;
}
// 使用向量化操作进行数组加法
FloatVector va = FloatVector.fromArray(species, a, 0);
FloatVector vb = FloatVector.fromArray(species, b, 0);
FloatVector result = va.add(vb);
// 将结果写回数组
result.intoArray(a, 0);
// 打印结果数组
for (float f : a) {
System.out.println(f);
}
}
}
原文链接:JDK21新特性及虚拟线程


![表情[baoquan]-拾光赋](https://blogs.ink/wp-content/themes/zibll/img/smilies/baoquan.gif)


暂无评论内容