Java21之虛擬線程用法實踐指南及常見問題
前言
虛擬線程是 Java 21(LTS)中正式引入的重大特性,屬于 Project Loom 的核心成果。它徹底改變了 Java 并發(fā)編程的范式。
1. 虛擬線程概述
1.1 什么是虛擬線程?
虛擬線程(Virtual Threads)是 JDK 實現(xiàn)的輕量級線程,由 JVM 而非操作系統(tǒng)管理。它們與傳統(tǒng)的平臺線程(Platform Threads,即操作系統(tǒng)線程)形成對比。
1.2 核心特點
- 輕量級:創(chuàng)建成本極低,可同時創(chuàng)建數(shù)百萬個
- 托管式:由 JVM 調(diào)度,而非操作系統(tǒng)
- 兼容性:完全兼容現(xiàn)有的
java.lang.ThreadAPI - 自動調(diào)度:在阻塞操作時自動掛起,釋放底層平臺線程
1.3 與平臺線程的對比
| 特性 | 平臺線程 | 虛擬線程 |
|---|---|---|
創(chuàng)建成本 | 高(MB 級內(nèi)存) | 極低(KB 級內(nèi)存) |
最大數(shù)量 | 幾千個 | 數(shù)百萬個 |
調(diào)度者 | 操作系統(tǒng) | JVM |
阻塞行為 | 阻塞整個 OS 線程 | 自動掛起,釋放載體線程 |
適用場景 | CPU 密集型任務(wù) | I/O 密集型任務(wù) |
2. 虛擬線程的創(chuàng)建方法
2.1 使用 Thread.ofVirtual()
// 方法1:直接創(chuàng)建并啟動
Thread virtualThread1 = Thread.ofVirtual().start(() -> {
System.out.println("Hello from virtual thread!");
});
// 方法2:構(gòu)建 Thread 對象后啟動
Thread virtualThread2 = Thread.ofVirtual().name("my-virtual-thread").unstarted(() -> {
System.out.println("Named virtual thread");
});
virtualThread2.start();
// 方法3:設(shè)置守護(hù)線程
Thread virtualThread3 = Thread.ofVirtual().daemon(true).unstarted(() -> {
System.out.println("Daemon virtual thread");
});2.2 使用 Thread.Builder
// 獲取虛擬線程構(gòu)建器
Thread.Builder builder = Thread.ofVirtual();
// 鏈?zhǔn)秸{(diào)用設(shè)置屬性
Thread virtualThread = builder
.name("custom-virtual-thread")
.daemon(false)
.unstarted(() -> {
System.out.println("Custom virtual thread running");
});
virtualThread.start();2.3 使用 Executors.newVirtualThreadPerTaskExecutor()
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// 創(chuàng)建虛擬線程執(zhí)行器
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
// 提交任務(wù),每個任務(wù)在一個新的虛擬線程中執(zhí)行
for (int i = 0; i < 10; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("Task " + taskId + " running on " + Thread.currentThread());
return "Result " + taskId;
});
}
} // 自動關(guān)閉執(zhí)行器2.4 從現(xiàn)有線程工廠創(chuàng)建
// 創(chuàng)建虛擬線程工廠
ThreadFactory virtualThreadFactory = Thread.ofVirtual().factory();
// 使用工廠創(chuàng)建線程
Thread thread = virtualThreadFactory.newThread(() -> {
System.out.println("Created via factory");
});
thread.start();3. 虛擬線程的核心使用場景
3.1 高并發(fā) I/O 操作
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class HighConcurrencyIO {
private static final HttpClient httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
public static void main(String[] args) throws InterruptedException {
// 模擬 10,000 個并發(fā) HTTP 請求
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
CompletableFuture<?>[] futures = new CompletableFuture[10000];
for (int i = 0; i < 10000; i++) {
final int requestId = i;
futures[i] = CompletableFuture.runAsync(() -> {
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/delay/1"))
.timeout(Duration.ofSeconds(5))
.build();
HttpResponse<String> response = httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println("Request " + requestId + " completed with status: "
+ response.statusCode());
} catch (Exception e) {
System.err.println("Request " + requestId + " failed: " + e.getMessage());
}
}, executor);
}
// 等待所有請求完成
CompletableFuture.allOf(futures).join();
}
System.out.println("All requests completed!");
}
}3.2 數(shù)據(jù)庫連接池優(yōu)化
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class DatabaseVirtualThreads {
private static final String DB_URL = "jdbc:postgresql://localhost:5432/test";
private static final String DB_USER = "user";
private static final String DB_PASSWORD = "password";
public static void main(String[] args) throws Exception {
// 傳統(tǒng)方式需要大量數(shù)據(jù)庫連接
// 虛擬線程方式:少量連接 + 大量虛擬線程
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
// 模擬 1000 個并發(fā)數(shù)據(jù)庫查詢
CompletableFuture<?>[] futures = new CompletableFuture[1000];
for (int i = 0; i < 1000; i++) {
final int queryId = i;
futures[i] = CompletableFuture.runAsync(() -> {
Connection conn = null;
try {
// 從連接池獲取連接(這里簡化為直接創(chuàng)建)
conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
PreparedStatement stmt = conn.prepareStatement(
"SELECT * FROM users WHERE id = ?");
stmt.setInt(1, queryId);
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
System.out.println("Query " + queryId + ": " + rs.getString("name"));
}
} catch (Exception e) {
System.err.println("Query " + queryId + " failed: " + e.getMessage());
} finally {
if (conn != null) {
try {
conn.close(); // 返回到連接池
} catch (Exception e) {
// ignore
}
}
}
}, executor);
}
CompletableFuture.allOf(futures).join();
}
}
}3.3 Web 服務(wù)器處理
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpExchange;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.util.concurrent.Executor;
public class VirtualThreadWebServer {
public static void main(String[] args) throws IOException {
// 創(chuàng)建 HTTP 服務(wù)器
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
// 設(shè)置虛擬線程執(zhí)行器處理每個請求
Executor virtualThreadExecutor = (runnable) -> {
Thread.ofVirtual().start(runnable);
};
server.setExecutor(virtualThreadExecutor);
// 添加處理器
server.createContext("/hello", new HttpHandler() {
@Override
public void handle(HttpExchange exchange) throws IOException {
// 模擬 I/O 操作(如數(shù)據(jù)庫查詢、外部 API 調(diào)用)
try {
Thread.sleep(100); // 模擬延遲
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
String response = "Hello from virtual thread! Thread: " +
Thread.currentThread();
exchange.sendResponseHeaders(200, response.length());
OutputStream os = exchange.getResponseBody();
os.write(response.getBytes());
os.close();
}
});
server.start();
System.out.println("Server started on http://localhost:8080/hello");
}
}4. 虛擬線程的高級特性
4.1 線程局部變量(ThreadLocal)
虛擬線程完全支持 ThreadLocal:
public class VirtualThreadLocalExample {
private static final ThreadLocal<String> context = new ThreadLocal<>();
public static void main(String[] args) throws InterruptedException {
Runnable task = () -> {
String threadName = Thread.currentThread().getName();
context.set("Context for " + threadName);
System.out.println("Thread: " + threadName +
", Context: " + context.get());
// 清理 ThreadLocal(最佳實踐)
context.remove();
};
// 在虛擬線程中使用 ThreadLocal
Thread vt1 = Thread.ofVirtual().name("VT-1").unstarted(task);
Thread vt2 = Thread.ofVirtual().name("VT-2").unstarted(task);
vt1.start();
vt2.start();
vt1.join();
vt2.join();
}
}4.2 線程中斷
虛擬線程支持標(biāo)準(zhǔn)的中斷機制:
public class VirtualThreadInterruptExample {
public static void main(String[] args) throws InterruptedException {
Thread virtualThread = Thread.ofVirtual().unstarted(() -> {
try {
System.out.println("Virtual thread started");
Thread.sleep(5000); // 可中斷的阻塞操作
System.out.println("Virtual thread finished normally");
} catch (InterruptedException e) {
System.out.println("Virtual thread was interrupted");
Thread.currentThread().interrupt(); // 保持中斷狀態(tài)
}
});
virtualThread.start();
// 1秒后中斷虛擬線程
Thread.sleep(1000);
virtualThread.interrupt();
virtualThread.join();
}
}4.3 線程棧跟蹤
虛擬線程提供完整的棧跟蹤信息:
public class VirtualThreadStackTrace {
public static void main(String[] args) {
Thread virtualThread = Thread.ofVirtual().unstarted(() -> {
methodA();
});
virtualThread.start();
try {
virtualThread.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
private static void methodA() {
methodB();
}
private static void methodB() {
// 打印當(dāng)前線程的棧跟蹤
Thread.dumpStack();
// 或者獲取棧跟蹤
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
System.out.println("Stack trace size: " + stackTrace.length);
for (StackTraceElement element : stackTrace) {
System.out.println(element);
}
}
}5. 虛擬線程的最佳實踐
5.1 何時使用虛擬線程
適合使用虛擬線程的場景:
- I/O 密集型任務(wù)(網(wǎng)絡(luò)請求、文件操作、數(shù)據(jù)庫查詢)
- 高并發(fā)服務(wù)器應(yīng)用
- 需要大量并發(fā)但每個任務(wù)較輕量的場景
不適合使用虛擬線程的場景:
- CPU 密集型任務(wù)(應(yīng)使用平臺線程池)
- 需要長時間持有鎖的任務(wù)
- 與 JNI 交互的本地代碼
5.2 性能調(diào)優(yōu)建議
// 1. 正確使用 try-with-resources 管理執(zhí)行器
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
// 提交任務(wù)
executor.submit(task);
} // 自動關(guān)閉,等待所有任務(wù)完成
// 2. 避免在虛擬線程中進(jìn)行 CPU 密集型計算
Runnable cpuIntensiveTask = () -> {
// 錯誤:在虛擬線程中進(jìn)行大量計算
// long result = heavyComputation();
// 正確:將 CPU 密集型任務(wù)提交到平臺線程池
ExecutorService cpuPool = Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors());
CompletableFuture.supplyAsync(() -> heavyComputation(), cpuPool)
.thenAccept(result -> {
// 處理結(jié)果(回到虛擬線程)
System.out.println("Result: " + result);
});
};
// 3. 合理管理資源
public class ResourceManagementExample {
private static final ExecutorService VIRTUAL_EXECUTOR =
Executors.newVirtualThreadPerTaskExecutor();
private static final ExecutorService CPU_EXECUTOR =
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
public static void shutdown() {
VIRTUAL_EXECUTOR.close();
CPU_EXECUTOR.shutdown();
}
}5.3 錯誤處理和監(jiān)控
public class VirtualThreadErrorHandling {
public static void main(String[] args) {
Thread virtualThread = Thread.ofVirtual().unstarted(() -> {
try {
// 可能拋出異常的操作
riskyOperation();
} catch (Exception e) {
// 處理異常
System.err.println("Exception in virtual thread: " + e.getMessage());
}
});
// 設(shè)置未捕獲異常處理器
virtualThread.setUncaughtExceptionHandler((thread, exception) -> {
System.err.println("Uncaught exception in " + thread.getName() +
": " + exception.getMessage());
});
virtualThread.start();
}
private static void riskyOperation() {
throw new RuntimeException("Something went wrong!");
}
}6. 虛擬線程的內(nèi)部機制
6.1 載體線程(Carrier Threads)
public class CarrierThreadExample {
public static void main(String[] args) throws InterruptedException {
Thread virtualThread = Thread.ofVirtual().unstarted(() -> {
System.out.println("Virtual thread: " + Thread.currentThread());
System.out.println("Carrier thread: " + Thread.currentThread().carrierThread());
// 模擬 I/O 阻塞
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("After sleep - Virtual: " + Thread.currentThread());
System.out.println("After sleep - Carrier: " + Thread.currentThread().carrierThread());
});
virtualThread.start();
virtualThread.join();
}
}6.2 掛起和恢復(fù)機制
當(dāng)虛擬線程遇到阻塞操作時:
- JVM 捕獲阻塞操作
- 虛擬線程被掛起
- 載體線程被釋放用于執(zhí)行其他虛擬線程
- 阻塞操作完成后,虛擬線程被調(diào)度到某個載體線程上恢復(fù)執(zhí)行
7. 與現(xiàn)有并發(fā)工具的集成
7.1 CompletableFuture
public class CompletableFutureWithVirtualThreads {
public static void main(String[] args) throws Exception {
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
CompletableFuture<String> future1 = CompletableFuture
.supplyAsync(() -> fetchDataFromAPI1(), executor);
CompletableFuture<String> future2 = CompletableFuture
.supplyAsync(() -> fetchDataFromAPI2(), executor);
CompletableFuture<Void> combined = CompletableFuture.allOf(future1, future2);
combined.join();
System.out.println("Data 1: " + future1.get());
System.out.println("Data 2: " + future2.get());
}
}
private static String fetchDataFromAPI1() {
// 模擬 API 調(diào)用
try { Thread.sleep(1000); } catch (InterruptedException e) {}
return "API1 Result";
}
private static String fetchDataFromAPI2() {
// 模擬 API 調(diào)用
try { Thread.sleep(1500); } catch (InterruptedException e) {}
return "API2 Result";
}
}7.2 Stream API
public class StreamWithVirtualThreads {
public static void main(String[] args) {
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
// 并行流通常使用 ForkJoinPool,但可以自定義
var results = IntStream.range(0, 1000)
.boxed()
.parallel()
.map(i -> processItem(i, executor))
.collect(Collectors.toList());
System.out.println("Processed " + results.size() + " items");
}
}
private static String processItem(int item, ExecutorService executor) {
// 在虛擬線程中處理每個項目
CompletableFuture<String> future = CompletableFuture
.supplyAsync(() -> {
// 模擬 I/O 操作
try { Thread.sleep(10); } catch (InterruptedException e) {}
return "Processed " + item;
}, executor);
return future.join();
}
}8. 調(diào)試和監(jiān)控虛擬線程
8.1 JVM 參數(shù)
# 啟用虛擬線程相關(guān)的調(diào)試信息 java -Djdk.virtualThreadScheduler.parallelism=8 YourApp # 監(jiān)控虛擬線程統(tǒng)計信息 java -XX:+UnlockDiagnosticVMOptions -XX:+LogVMOutput YourApp
8.2 JMX 監(jiān)控
import java.lang.management.ManagementFactory;
import com.sun.management.ThreadMXBean;
public class VirtualThreadMonitoring {
public static void main(String[] args) {
ThreadMXBean threadBean = (ThreadMXBean) ManagementFactory.getThreadMXBean();
// 獲取線程信息
long[] threadIds = threadBean.getAllThreadIds();
System.out.println("Total threads: " + threadIds.length);
// 虛擬線程的線程 ID 通常是負(fù)數(shù)
for (long id : threadIds) {
if (id < 0) {
System.out.println("Virtual thread ID: " + id);
}
}
}
}8.3 日志記錄
public class VirtualThreadLogging {
private static final Logger logger = LoggerFactory.getLogger(VirtualThreadLogging.class);
public static void main(String[] args) {
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 10; i++) {
final int taskId = i;
executor.submit(() -> {
// 日志中包含線程信息
logger.info("Processing task {} on thread {}",
taskId, Thread.currentThread().getName());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
logger.info("Completed task {}", taskId);
});
}
}
}
}9. 常見問題和解決方案
9.1 內(nèi)存泄漏問題
// 問題:ThreadLocal 未清理
private static final ThreadLocal<ExpensiveResource> resource = new ThreadLocal<>();
public void badExample() {
Thread.ofVirtual().start(() -> {
resource.set(new ExpensiveResource()); // 未清理!
// ... do work
});
}
// 解決方案:確保清理 ThreadLocal
public void goodExample() {
Thread.ofVirtual().start(() -> {
try {
resource.set(new ExpensiveResource());
// ... do work
} finally {
resource.remove(); // 確保清理
}
});
}9.2 死鎖檢測
虛擬線程的死鎖檢測與平臺線程相同:
public class DeadlockExample {
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
public static void main(String[] args) {
Thread t1 = Thread.ofVirtual().unstarted(() -> {
synchronized (lock1) {
System.out.println("T1 got lock1");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lock2) {
System.out.println("T1 got lock2");
}
}
});
Thread t2 = Thread.ofVirtual().unstarted(() -> {
synchronized (lock2) {
System.out.println("T2 got lock2");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lock1) {
System.out.println("T2 got lock1");
}
}
});
t1.start();
t2.start();
// JVM 仍然可以檢測到死鎖
}
}10. 總結(jié)
10.1 虛擬線程的優(yōu)勢
- 極高的并發(fā)能力:輕松支持百萬級并發(fā)
- 簡化的編程模型:無需復(fù)雜的異步回調(diào)
- 向后兼容:完全兼容現(xiàn)有 Thread API
- 自動資源管理:阻塞時自動釋放載體線程
10.2 使用建議
- I/O 密集型任務(wù):優(yōu)先使用虛擬線程
- CPU 密集型任務(wù):繼續(xù)使用平臺線程池
- 資源管理:注意 ThreadLocal 清理和資源釋放
- 監(jiān)控調(diào)試:利用現(xiàn)有的 JVM 工具進(jìn)行監(jiān)控
虛擬線程是 Java 并發(fā)編程的重大突破,它讓開發(fā)者能夠以同步的方式編寫高并發(fā)代碼,同時獲得異步編程的性能優(yōu)勢。掌握虛擬線程的使用方法,將極大地提升 Java 應(yīng)用的并發(fā)處理能力。
到此這篇關(guān)于Java21之虛擬線程用法實踐指南及常見問題的文章就介紹到這了,更多相關(guān)Java21虛擬線程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot實現(xiàn)ThreadLocal父子線程傳值的幾種方式
ThreadLocal作為Java中重要的線程本地變量機制,為我們提供了在單個線程內(nèi)存儲數(shù)據(jù)的便利,但是,當(dāng)涉及到父子線程之間的數(shù)據(jù)傳遞時,ThreadLocal默認(rèn)的行為并不能滿足我們的需求,所以本文將介紹在SpringBoot應(yīng)用中實現(xiàn)ThreadLocal父子線程傳值的幾種方式2025-12-12
Java C++題解leetcode 1684統(tǒng)計一致字符串的數(shù)目示例
這篇文章主要為大家介紹了Java C++題解leetcode 1684統(tǒng)計一致字符串的數(shù)目示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
Java String字符串內(nèi)容實現(xiàn)添加雙引號
這篇文章主要介紹了Java String字符串內(nèi)容實現(xiàn)添加雙引號,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09
解決Spring Security的權(quán)限配置不生效問題
這篇文章主要介紹了解決Spring Security的權(quán)限配置不生效問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03
Java使用Callable接口實現(xiàn)多線程的實例代碼
這篇文章主要介紹了Java使用Callable接口實現(xiàn)多線程的實例代碼,實現(xiàn)Callable和實現(xiàn)Runnable類似,但是功能更強大,具體表現(xiàn)在可以在任務(wù)結(jié)束后提供一個返回值,Runnable不行,call方法可以拋出異,Runnable的run方法不行,需要的朋友可以參考下2023-08-08
SpringBoot應(yīng)用War包形式部署到外部Tomcat的方法
這篇文章主要介紹了SpringBoot應(yīng)用War包形式部署到外部Tomcat的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-08-08
Spring Boot Actuator端點相關(guān)原理解析
這篇文章主要介紹了Spring Boot Actuator端點相關(guān)原理解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-07-07

