Java 虚拟线程是 JDK 21 引入的重要并发特性,通过轻量级线程实现,能够以极低的资源成本支持百万级并发。本文深入分析虚拟线程的工作原理、使用方法和性能优化技术。
1. 虚拟线程基础与原理
1.1 核心概念
虚拟线程是 Java 平台线程模型的轻量级实现,由 JVM 管理而非直接由操作系统管理。它们采用 M:N 调度模型,将大量虚拟线程映射到少量操作系统线程上。
1.2 Project Loom 与版本演进
虚拟线程是 Project Loom 项目的核心部分,该项目旨在彻底改革 Java 并发模型。Loom 不仅包括虚拟线程,还包括结构化并发 API 和作用域变量等特性。
- JDK 19: 首次引入预览特性 (JEP 425)
- JDK 20: 第二轮预览,改进 API 和性能 (JEP 436)
- JDK 21: 正式发布为标准特性 (JEP 444)
1.3 工作原理
虚拟线程采用协作式调度机制,核心是挂起和恢复操作:
1.3.1 协作式调度 vs 抢占式调度
特性 | 协作式调度(虚拟线程) | 抢占式调度(平台线程) |
---|---|---|
线程切换时机 | 线程在挂起点主动让出 CPU | 操作系统强制中断线程执行 |
调度控制 | JVM 负责调度 | 操作系统调度器控制 |
上下文切换开销 | 轻量级(仅保存 JVM 状态) | 重量级(保存完整 CPU 状态) |
资源效率 | 高(一个 OS 线程支持多个虚拟线程) | 低(每个线程需要独立 OS 资源) |
CPU 密集型任务 | 可能阻塞其他虚拟线程 | 时间片机制防止单线程独占 CPU |
1.3.2 延续机制
虚拟线程的底层实现依赖于 JDK 中的 Continuation API,用于捕获和恢复执行状态:
// 简化的内部实现原理示意
class VirtualThread {
private Continuation continuation;
VirtualThread(Runnable task) {
// 创建延续,封装任务执行状态
this.continuation = new Continuation(
(scope) -> {
task.run();
return null;
},
Continuation.Scope.FULL
);
}
void run() {
// 遇到挂起点时,保存当前状态并返回
continuation.run();
}
boolean isYieldPoint() {
// 检测当前执行点是否为挂起点
return continuation.isPreempted();
}
}
1.3.3 挂起点识别
识别代码中可能导致线程固定的方法:
import jdk.internal.vm.Continuation;
import jdk.internal.vm.ContinuationScope;
// 注意:需要--add-exports java.base/jdk.internal.vm=ALL-UNNAMED运行选项
public class PinningDetector {
public static boolean isPinningOperation(Runnable operation) {
final ContinuationScope scope = new ContinuationScope("TEST");
final boolean[] pinned = {false};
Continuation cont = new Continuation(scope, () -> {
operation.run();
});
// 启动延续
cont.run();
// 如果无法挂起,则是固定操作
return !cont.isPreempted();
}
}
1.3.4 虚拟线程状态转换
虚拟线程的生命周期包含以下状态转换:
2. 创建和使用虚拟线程
JDK 21 提供多种创建方式,推荐使用 ExecutorService 方式管理生命周期:
import java.time.Duration;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.stream.IntStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import java.util.UUID;
public class VirtualThreadDemo {
private static final Logger logger = LoggerFactory.getLogger(VirtualThreadDemo.class);
public static void main(String[] args) {
// 方法1:直接启动单个虚拟线程
Thread vThread = Thread.startVirtualThread(() -> {
// 添加MDC上下文,提高日志可追踪性
String threadId = UUID.randomUUID().toString();
MDC.put("threadId", threadId);
try {
logger.info("执行于: {}", Thread.currentThread());
} finally {
MDC.remove("threadId");
}
});
// 方法2:使用Builder API
Thread vThread2 = Thread.ofVirtual()
.name("custom-vthread")
.uncaughtExceptionHandler((t, e) ->
logger.error("线程 {} 发生异常", t.getName(), e))
.start(() -> {
logger.info("命名虚拟线程执行");
});
// 方法3:使用ExecutorService(推荐用于管理大量虚拟线程)
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
// 提交10,000个任务,每个都在独立的虚拟线程中执行
IntStream.range(0, 10_000).forEach(i -> {
Future<Integer> future = executor.submit(() -> {
try {
// 模拟I/O操作
Thread.sleep(Duration.ofMillis(100));
return i;
} catch (InterruptedException e) {
// 正确处理中断
Thread.currentThread().interrupt();
logger.warn("任务{}被中断", i, e);
return -1;
}
});
});
} // ExecutorService自动关闭
logger.info("所有任务已提交");
}
// 虚拟线程调试辅助方法
public static void dumpVirtualThreadInfo() {
if (logger.isDebugEnabled()) {
Thread.getAllStackTraces().entrySet().stream()
.filter(e -> e.getKey().isVirtual())
.forEach(e -> {
Thread t = e.getKey();
logger.debug("虚拟线程: {} (ID: {}), 状态: {}",
t.getName(), t.threadId(), t.getState());
for (StackTraceElement ste : e.getValue()) {
logger.debug(" at {}", ste);
}
});
}
}
}
3. 性能优化最佳方法
3.1 适用场景分析
3.2 HTTP 客户端优化示例
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
public class OptimizedHttpClient {
private static final Logger logger = LoggerFactory.getLogger(OptimizedHttpClient.class);
public static void main(String[] args) throws Exception {
// 创建HTTP客户端
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5))
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
List<String> urls = List.of(
"https://example.com",
"https://example.org",
"https://example.net"
);
long startTime = System.currentTimeMillis();
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
List<Future<String>> futures = new ArrayList<>();
// 提交所有HTTP请求任务
for (String url : urls) {
futures.add(executor.submit(() -> {
// 添加MDC上下文
String requestId = UUID.randomUUID().toString();
MDC.put("requestId", requestId);
try {
logger.info("开始请求: {}", url);
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.timeout(Duration.ofSeconds(10))
.header("User-Agent", "Java Virtual Threads Demo")
.header("X-Request-ID", requestId)
.GET()
.build();
HttpResponse<String> response = client.send(
request, HttpResponse.BodyHandlers.ofString());
logger.info("完成请求: {}, 状态码: {}", url, response.statusCode());
return String.format("URL: %s, 状态: %d, 内容长度: %d",
url, response.statusCode(), response.body().length());
} catch (IOException e) {
logger.error("请求{}时发生I/O错误", url, e);
return String.format("URL: %s, 错误: I/O异常", url);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
logger.warn("请求{}时被中断", url, e);
return String.format("URL: %s, 错误: 被中断", url);
} catch (Exception e) {
logger.error("请求{}时发生未预期错误", url, e);
return String.format("URL: %s, 错误: %s", url, e.getMessage());
}
} finally {
MDC.remove("requestId");
}
}));
}
// 获取所有结果,添加超时处理
for (Future<String> future : futures) {
try {
logger.info(future.get(15, TimeUnit.SECONDS));
} catch (TimeoutException e) {
logger.warn("获取结果超时", e);
}
}
}
long duration = System.currentTimeMillis() - startTime;
logger.info("总执行时间: {}ms", duration);
}
}
3.3 JDBC 操作优化
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
public class OptimizedJdbcExample {
private static final Logger logger = LoggerFactory.getLogger(OptimizedJdbcExample.class);
// 使用HikariCP等连接池
private final DataSource dataSource;
public OptimizedJdbcExample(DataSource dataSource) {
this.dataSource = dataSource;
}
public List<String> fetchUsernames(List<Integer> userIds) throws Exception {
List<String> results = new ArrayList<>();
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
List<Future<String>> futures = new ArrayList<>();
// 为每个ID创建单独的查询任务
for (Integer userId : userIds) {
futures.add(executor.submit(() -> {
String queryId = UUID.randomUUID().toString();
MDC.put("queryId", queryId);
try {
logger.debug("开始查询用户ID: {}", userId);
// 使用try-with-resources管理连接
try (Connection conn = dataSource.getConnection()) {
// 注意:此优化特定于MySQL 8.0+驱动,其他数据库驱动可能需要不同配置
if (conn.isWrapperFor(com.mysql.cj.jdbc.JdbcConnection.class)) {
conn.unwrap(com.mysql.cj.jdbc.JdbcConnection.class)
.getPropertySet()
.getBooleanProperty("useVirtualThreads")
.setValue(true);
}
// 设置连接超时
conn.setNetworkTimeout(Executors.newVirtualThreadPerTaskExecutor(), 5000);
String sql = "SELECT username FROM users WHERE id = ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
// 分开设置参数与执行,提高可读性和调试便捷性
stmt.setInt(1, userId);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
return rs.getString("username");
}
return null;
}
}
} catch (SQLException e) {
logger.error("查询用户ID={}时发生SQL错误", userId, e);
return null;
}
} finally {
MDC.remove("queryId");
}
}));
}
// 收集结果,添加超时处理
for (Future<String> future : futures) {
try {
String username = future.get(10, TimeUnit.SECONDS);
if (username != null) {
results.add(username);
}
} catch (Exception e) {
logger.warn("获取用户名结果失败", e);
}
}
}
return results;
}
}
3.4 文件 I/O 优化示例
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class OptimizedFileIO {
private static final Logger logger = LoggerFactory.getLogger(OptimizedFileIO.class);
public static List<String> processFiles(List<Path> filePaths) {
List<String> results = new ArrayList<>();
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
List<Future<String>> futures = new ArrayList<>();
for (Path path : filePaths) {
futures.add(executor.submit(() -> {
String fileId = UUID.randomUUID().toString();
MDC.put("fileId", fileId);
try {
logger.debug("开始处理文件: {}", path.getFileName());
// 使用异步文件通道避免阻塞
try (AsynchronousFileChannel channel = AsynchronousFileChannel.open(
path, StandardOpenOption.READ)) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
StringBuilder content = new StringBuilder();
// 读取文件内容
int position = 0;
Future<Integer> readFuture;
do {
buffer.clear();
readFuture = channel.read(buffer, position);
int bytesRead = readFuture.get();
if (bytesRead <= 0) break;
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
content.append(new String(bytes));
position += bytesRead;
} while (true);
logger.debug("完成文件处理: {}, 读取字节数: {}", path.getFileName(), position);
return path.getFileName() + ": " + content.substring(0,
Math.min(50, content.length())) + "...";
}
} catch (IOException e) {
logger.error("读取文件{}时出错", path, e);
return path.getFileName() + ": 读取错误";
} catch (Exception e) {
logger.error("处理文件{}时出错", path, e);
return path.getFileName() + ": 处理错误";
} finally {
MDC.remove("fileId");
}
}));
}
for (Future<String> future : futures) {
try {
results.add(future.get());
} catch (Exception e) {
logger.error("获取文件处理结果时出错", e);
}
}
}
return results;
}
}
3.5 消息队列处理示例
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MessageQueueProcessor {
private static final Logger logger = LoggerFactory.getLogger(MessageQueueProcessor.class);
// 使用信号量实现背压机制
private final Semaphore concurrencyLimiter;
private final AtomicInteger activeThreads = new AtomicInteger(0);
public MessageQueueProcessor(int maxConcurrency) {
this.concurrencyLimiter = new Semaphore(maxConcurrency);
}
public void startProcessing(MessageQueue queue) {
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
while (!Thread.currentThread().isInterrupted()) {
// 实现背压控制
if (concurrencyLimiter.tryAcquire()) {
try {
Message message = queue.poll();
if (message == null) {
concurrencyLimiter.release();
Thread.sleep(100); // 队列为空时短暂暂停
continue;
}
// 提交处理任务
executor.submit(() -> {
String messageId = message.getId();
MDC.put("messageId", messageId);
try {
int current = activeThreads.incrementAndGet();
logger.info("开始处理消息ID={}, 当前活动线程: {}",
messageId, current);
processMessageWithRetry(message);
} catch (Exception e) {
logger.error("处理消息{}时出错", messageId, e);
// 根据业务需求处理失败消息
queue.retry(message);
} finally {
activeThreads.decrementAndGet();
concurrencyLimiter.release();
MDC.remove("messageId");
}
});
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
} else {
// 等待资源释放
Thread.sleep(50);
}
}
} catch (Exception e) {
logger.error("消息处理主循环异常", e);
}
}
// 带指数退避的重试机制
private void processMessageWithRetry(Message message) throws Exception {
int attempts = 0;
int maxAttempts = 3;
long initialDelayMs = 100;
while (attempts < maxAttempts) {
try {
processMessage(message);
return; // 成功处理,返回
} catch (Exception e) {
attempts++;
if (attempts >= maxAttempts) {
throw e; // 重试次数用尽,抛出异常
}
// 计算指数退避延迟时间
long delayMs = initialDelayMs * (1L << (attempts - 1));
logger.warn("处理消息{}失败,将在{}ms后进行第{}次重试",
message.getId(), delayMs, attempts + 1);
Thread.sleep(delayMs);
}
}
}
private void processMessage(Message message) {
// 消息处理逻辑
}
// 接口定义
interface MessageQueue {
Message poll();
void retry(Message message);
}
interface Message {
String getId();
byte[] getPayload();
}
}
3.6 虚拟线程与响应式编程对比
import reactor.core.publisher.Flux;
import java.util.List;
import java.util.concurrent.Executors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ReactiveVsVirtualThreads {
private static final Logger logger = LoggerFactory.getLogger(ReactiveVsVirtualThreads.class);
public static void main(String[] args) {
List<String> urls = List.of(
"https://example.com",
"https://example.org",
"https://example.net"
);
// 响应式编程方式
logger.info("使用响应式编程处理...");
Flux.fromIterable(urls)
.flatMap(url -> makeReactiveHttpRequest(url))
.doOnNext(response -> logger.info("响应式响应: {}", response))
.subscribe();
// 虚拟线程方式
logger.info("使用虚拟线程处理...");
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (String url : urls) {
executor.submit(() -> {
String response = makeBlockingHttpRequest(url);
logger.info("虚拟线程响应: {}", response);
return response;
});
}
}
}
// 模拟方法
private static Flux<String> makeReactiveHttpRequest(String url) {
return Flux.just("Response from " + url);
}
private static String makeBlockingHttpRequest(String url) {
return "Response from " + url;
}
}
4. 常见问题与解决方案
4.1 线程固定问题
当虚拟线程在执行某些操作时无法被挂起,会导致承载线程被固定,降低性能。
线程固定原因和解决方案
原因 | 问题描述 | 解决方案 |
---|---|---|
同步块 | synchronized 块阻止虚拟线程挂起 | 使用 java.util.concurrent 中的锁代替 |
原生方法 | JNI 代码执行期间无法挂起 | 减少原生方法调用或使用异步包装 |
旧版 JDBC | 同步阻塞驱动会固定线程 | 使用支持虚拟线程的 JDBC 驱动或 R2DBC |
遗留代码 | 使用 Thread.currentThread()的旧代码 | 重构为使用上下文或参数传递 |
监视器操作 | Object.wait()等方法 | 使用 concurrent 包中的同步工具 |
错误示例:
// 不推荐:在虚拟线程中使用同步锁
void processData(List<String> data) {
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (String item : data) {
executor.submit(() -> {
synchronized (databaseLock) { // 会导致承载线程被固定
performDatabaseOperation(item);
return true;
}
});
}
}
}
改进方案:
import java.util.concurrent.locks.ReentrantLock;
// 推荐:使用java.util.concurrent锁
private final ReentrantLock databaseLock = new ReentrantLock();
void processData(List<String> data) {
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (String item : data) {
executor.submit(() -> {
// ReentrantLock允许虚拟线程在等待锁时挂起
databaseLock.lock();
try {
performDatabaseOperation(item);
return true;
} finally {
databaseLock.unlock();
}
});
}
}
}
4.2 ThreadLocal 使用问题
虚拟线程拥有自己的 ThreadLocal 变量,需要注意以下几点:
- 每个虚拟线程有独立的 ThreadLocal 存储空间
- 虚拟线程数量大时,ThreadLocal 可能导致内存问题
- 继承式 ThreadLocal(InheritableThreadLocal)在虚拟线程间可能表现不一致
import java.util.concurrent.Executors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.ExecutorService;
public class ThreadLocalExample {
private static final Logger logger = LoggerFactory.getLogger(ThreadLocalExample.class);
private static final ThreadLocal<String> userContext = new ThreadLocal<>();
public static void main(String[] args) throws Exception {
userContext.set("MainUser");
logger.info("主线程用户: {}", userContext.get());
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> {
// 虚拟线程有自己的ThreadLocal空间
logger.info("虚拟线程初始用户: {}", userContext.get()); // 输出null
userContext.set("VirtualUser");
logger.info("虚拟线程设置后用户: {}", userContext.get()); // 输出VirtualUser
return null;
}).get();
}
logger.info("主线程用户(不变): {}", userContext.get()); // 仍然是MainUser
// 重要:大量虚拟线程时,应确保ThreadLocal正确清理
userContext.remove();
}
// 检测ThreadLocal内存泄漏
public static void checkForThreadLocalLeaks() {
System.gc();
// 获取JVM内存使用情况
Runtime runtime = Runtime.getRuntime();
long usedMemory = runtime.totalMemory() - runtime.freeMemory();
logger.info("当前内存使用: {} MB", usedMemory / (1024 * 1024));
// 如果使用Java Mission Control或JFR,可以监控ThreadLocal实例数量
}
}
4.3 死锁检测与预防
虚拟线程的死锁情况可能更难检测,因为传统工具可能无法正确显示其状态:
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DeadlockDetector {
private static final Logger logger = LoggerFactory.getLogger(DeadlockDetector.class);
// 定期检测死锁
public static void startDeadlockDetection(long intervalMs) {
Thread detector = Thread.ofVirtual().name("deadlock-detector").start(() -> {
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
while (!Thread.currentThread().isInterrupted()) {
try {
long[] deadlockedThreadIds = threadBean.findDeadlockedThreads();
if (deadlockedThreadIds != null && deadlockedThreadIds.length > 0) {
logger.error("检测到死锁! 涉及{}个线程", deadlockedThreadIds.length);
ThreadInfo[] threadInfos = threadBean.getThreadInfo(deadlockedThreadIds, true, true);
for (ThreadInfo info : threadInfos) {
logger.error("死锁线程: {}\n锁持有情况: {}\n锁等待情况: {}",
info.getThreadName(),
info.getLockName(),
info.getLockInfo());
}
}
Thread.sleep(intervalMs);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
});
}
// 死锁预防示例:总是按相同顺序获取锁
public static class SafeLocking {
private final ReentrantLock lockA = new ReentrantLock();
private final ReentrantLock lockB = new ReentrantLock();
// 安全方法:总是按固定顺序获取锁
public void safeMethod() {
lockA.lock();
try {
lockB.lock();
try {
// 操作共享资源
} finally {
lockB.unlock();
}
} finally {
lockA.unlock();
}
}
}
}
4.4 监控和调试
虚拟线程的特殊性质需要不同的监控和调试方法:
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.Arrays;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordingStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VirtualThreadMonitoring {
private static final Logger logger = LoggerFactory.getLogger(VirtualThreadMonitoring.class);
public static void startMonitoring() {
// 使用JDK Flight Recorder监控虚拟线程
RecordingStream rs = new RecordingStream();
// 监控虚拟线程创建
rs.enable("jdk.VirtualThreadStart");
rs.enable("jdk.VirtualThreadEnd");
// 监控线程固定
rs.enable("jdk.VirtualThreadPinned");
rs.onEvent("jdk.VirtualThreadStart", event -> {
logger.info("虚拟线程启动: {}", event.getString("threadName"));
});
rs.onEvent("jdk.VirtualThreadPinned", event -> {
logger.warn("虚拟线程固定: {}, 持续时间: {}ms, 原因: {}",
event.getString("threadName"),
event.getDuration().toMillis(),
event.getString("reason", "未知"));
});
rs.start();
}
public static void analyzeThreadDump() {
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
// 获取包含虚拟线程的线程转储
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(true, true);
// 分析虚拟线程
long virtualThreadCount = Arrays.stream(threadInfos)
.filter(info -> info.getThreadName().startsWith("VirtualThread"))
.count();
// 统计线程状态
long runningCount = Arrays.stream(threadInfos)
.filter(info -> info.getThreadState() == Thread.State.RUNNABLE)
.count();
long waitingCount = Arrays.stream(threadInfos)
.filter(info -> info.getThreadState() == Thread.State.WAITING ||
info.getThreadState() == Thread.State.TIMED_WAITING)
.count();
long blockedCount = Arrays.stream(threadInfos)
.filter(info -> info.getThreadState() == Thread.State.BLOCKED)
.count();
logger.info("线程统计: 总计={}, 虚拟线程={}, 运行中={}, 等待中={}, 阻塞={}",
threadInfos.length, virtualThreadCount, runningCount, waitingCount, blockedCount);
}
// 检测虚拟线程栈溢出风险
public static void checkForStackOverflowRisks() {
if (logger.isDebugEnabled()) {
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
Arrays.stream(threadInfos)
.filter(info -> info.getThreadName().startsWith("VirtualThread"))
.forEach(info -> {
StackTraceElement[] stack = info.getStackTrace();
if (stack.length > 100) { // 设置栈深度阈值
logger.debug("虚拟线程{}栈深度较大({}帧),可能存在栈溢出风险",
info.getThreadName(), stack.length);
}
});
}
}
}
4.5 熔断器模式实现
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CircuitBreaker {
private static final Logger logger = LoggerFactory.getLogger(CircuitBreaker.class);
// 使用枚举定义熔断器状态
public enum CircuitState {
CLOSED, // 正常状态,允许请求通过
OPEN, // 熔断状态,拒绝所有请求
HALF_OPEN // 半开状态,允许少量请求尝试恢复
}
private final AtomicInteger failureCount = new AtomicInteger(0);
private final AtomicReference<CircuitState> state = new AtomicReference<>(CircuitState.CLOSED);
private final int threshold;
private final long resetTimeoutMs;
private volatile long lastFailureTime;
private final String name;
public CircuitBreaker(String name, int threshold, long resetTimeoutMs) {
this.name = name;
this.threshold = threshold;
this.resetTimeoutMs = resetTimeoutMs;
}
public <T> T execute(Supplier<T> operation) throws CircuitBreakerOpenException, Exception {
CircuitState currentState = state.get();
if (currentState == CircuitState.OPEN) {
// 检查是否可以尝试半开状态
if (System.currentTimeMillis() - lastFailureTime > resetTimeoutMs) {
logger.info("熔断器[{}]尝试半开状态", name);
// 尝试转换到半开状态
if (state.compareAndSet(CircuitState.OPEN, CircuitState.HALF_OPEN)) {
failureCount.set(0);
}
} else {
logger.warn("熔断器[{}]断开中,拒绝请求", name);
throw new CircuitBreakerOpenException("Circuit breaker is open");
}
}
try {
T result = operation.get();
// 成功执行,重置失败计数并确保关闭状态
successfulCall();
return result;
} catch (Exception e) {
// 记录失败
recordFailure();
throw e;
}
}
private void successfulCall() {
failureCount.set(0);
CircuitState currentState = state.get();
if (currentState != CircuitState.CLOSED) {
if (state.compareAndSet(currentState, CircuitState.CLOSED)) {
logger.info("熔断器[{}]关闭,服务恢复正常", name);
}
}
}
private void recordFailure() {
lastFailureTime = System.currentTimeMillis();
int currentFailures = failureCount.incrementAndGet();
logger.warn("熔断器[{}]记录失败,当前失败次数: {}/{}", name, currentFailures, threshold);
if (currentFailures >= threshold) {
CircuitState currentState = state.get();
if (currentState != CircuitState.OPEN &&
state.compareAndSet(currentState, CircuitState.OPEN)) {
logger.error("熔断器[{}]打开,暂停服务{}ms", name, resetTimeoutMs);
}
}
}
// 熔断器状态
public CircuitState getState() {
return state.get();
}
public int getFailureCount() {
return failureCount.get();
}
// 专用异常类型
public static class CircuitBreakerOpenException extends Exception {
public CircuitBreakerOpenException(String message) {
super(message);
}
}
}
4.6 线程安全的资源管理器
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 线程安全的资源管理器,确保资源只被关闭一次
*/
public final class ResourceGuard<T extends AutoCloseable> implements AutoCloseable {
private static final Logger logger = LoggerFactory.getLogger(ResourceGuard.class);
private final T resource;
private final AtomicBoolean closed = new AtomicBoolean(false);
private final String resourceName;
public ResourceGuard(T resource) {
this(resource, resource.getClass().getSimpleName());
}
public ResourceGuard(T resource, String resourceName) {
this.resource = resource;
this.resourceName = resourceName;
}
/**
* 获取被管理的资源
* @throws IllegalStateException 如果资源已关闭
*/
public T get() {
if (closed.get()) {
throw new IllegalStateException("Resource " + resourceName + " already closed");
}
return resource;
}
/**
* 安全关闭资源,确保只关闭一次
*/
@Override
public void close() throws Exception {
if (closed.compareAndSet(false, true)) {
try {
resource.close();
logger.debug("资源[{}]已安全关闭", resourceName);
} catch (Exception e) {
logger.error("关闭资源[{}]时发生错误", resourceName, e);
throw e;
}
} else {
logger.debug("资源[{}]已经关闭,忽略重复关闭请求", resourceName);
}
}
/**
* 检查资源是否已关闭
*/
public boolean isClosed() {
return closed.get();
}
}
5. 与框架整合
5.1 Spring Boot 整合
Spring Boot 3.2+原生支持虚拟线程:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.embedded.tomcat.TomcatProtocolHandlerCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.filter.CommonsRequestLoggingFilter;
import java.util.concurrent.Executors;
import jakarta.servlet.Filter;
@SpringBootApplication
@EnableAsync
public class VirtualThreadSpringApplication {
public static void main(String[] args) {
SpringApplication.run(VirtualThreadSpringApplication.class, args);
}
@Bean
public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
// 配置Tomcat使用虚拟线程处理请求
return protocolHandler -> {
protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
};
}
// 配置Spring @Async使用虚拟线程
@Bean
public java.util.concurrent.Executor taskExecutor() {
return Executors.newVirtualThreadPerTaskExecutor();
}
// 添加请求日志过滤器
@Bean
public Filter requestLoggingFilter() {
CommonsRequestLoggingFilter filter = new CommonsRequestLoggingFilter();
filter.setIncludeClientInfo(true);
filter.setIncludeQueryString(true);
filter.setIncludePayload(true);
filter.setMaxPayloadLength(10000);
filter.setIncludeHeaders(false);
return filter;
}
// 全局异常处理
@Bean
public GlobalExceptionHandler globalExceptionHandler() {
return new GlobalExceptionHandler();
}
// 或者使用application.properties配置:
// spring.threads.virtual.enabled=true
@RestController
public static class VirtualThreadController {
@GetMapping("/blocking")
public String blockingEndpoint() throws InterruptedException {
// 直接在虚拟线程中执行阻塞操作
Thread.sleep(1000);
return "处理完成,线程: " + Thread.currentThread();
}
@Async
public void asyncMethod() {
// 此方法将在虚拟线程中执行
}
}
}
到目前为止,我们已经深入探讨了虚拟线程的基础原理、创建方法、性能优化技巧、常见问题解决方案以及与主流框架的整合。这些内容为您提供了使用 Java 虚拟线程的基础。
在下一部分中,我们将继续深入探讨虚拟线程的高级应用场景,包括从传统线程池迁移的策略、大规模生产环境部署建议、内部实现原理分析、性能测试对比以及与其他并发模型的对比。还将提供更多实战案例,帮助您在实际项目中充分发挥虚拟线程的潜力。