一、概述
java 21引入了虚拟线程(virtual threads),这是project loom的核心特性。虚拟线程是轻量级线程,可以显著提高应用程序的并发处理能力,特别适合i/o密集型任务。
在spring boot 3.2+中,已经内置了对虚拟线程的支持,可以通过简单的配置启用。
二、spring boot 3.2+ 虚拟线程自动配置
1. 启用虚拟线程(已在项目中配置)
在 application.yml 中已经配置:
spring:
threads:
virtual:
enabled: true
这个配置会自动:
- 为
@async方法启用虚拟线程执行器 - 为 spring mvc 的请求处理启用虚拟线程
- 为 spring webflux 启用虚拟线程
2. 验证虚拟线程是否启用
可以通过以下方式验证:
@springboottest
public class virtualthreadtest {
@test
public void testvirtualthread() {
thread thread = thread.ofvirtual().start(() -> {
system.out.println("虚拟线程名称: " + thread.currentthread().getname());
system.out.println("是否为虚拟线程: " + thread.currentthread().isvirtual());
});
try {
thread.join();
} catch (interruptedexception e) {
thread.currentthread().interrupt();
}
}
}
三、使用方式
方式一:spring boot自动配置(推荐)
1.1 异步方法中使用虚拟线程
配置类改造(推荐):
/**
* 异步线程池配置(虚拟线程版本)
* 当 spring.threads.virtual.enabled=true 时使用虚拟线程
*
* @author
* @date 2024-10-31
*/
@slf4j
@configuration
@enableasync
public class virtualthreadasyncconfig implements asyncconfigurer {
/**
* 虚拟线程异步执行器
* spring boot 3.2+ 会自动创建虚拟线程执行器,这里提供手动配置示例
*/
@bean(name = "virtualthreadexecutor")
@conditionalonproperty(name = "spring.threads.virtual.enabled", havingvalue = "true", matchifmissing = false)
public executor virtualthreadexecutor() {
return new taskexecutoradapter(executors.newvirtualthreadpertaskexecutor());
}
/**
* 默认异步执行器
* 如果启用虚拟线程,spring boot会自动使用虚拟线程执行器
*/
@override
public executor getasyncexecutor() {
// spring boot 3.2+ 会自动使用虚拟线程执行器(如果启用)
// 如果需要手动指定,可以返回 virtualthreadexecutor()
return executors.newvirtualthreadpertaskexecutor();
}
@override
public asyncuncaughtexceptionhandler getasyncuncaughtexceptionhandler() {
return new simpleasyncuncaughtexceptionhandler();
}
}
使用示例:
@service
@slf4j
public class exampleservice {
/**
* 使用默认虚拟线程执行器
*/
@async
public completablefuture<string> asyncmethod1() {
log.info("当前线程: {}, 是否为虚拟线程: {}",
thread.currentthread().getname(),
thread.currentthread().isvirtual());
// 执行异步任务
return completablefuture.completedfuture("完成");
}
/**
* 指定使用虚拟线程执行器
*/
@async("virtualthreadexecutor")
public completablefuture<string> asyncmethod2() {
log.info("当前线程: {}, 是否为虚拟线程: {}",
thread.currentthread().getname(),
thread.currentthread().isvirtual());
return completablefuture.completedfuture("完成");
}
}
1.2 web请求处理中使用虚拟线程
spring boot 3.2+ 启用虚拟线程后,所有web请求会自动使用虚拟线程处理,无需额外配置。
验证方式:
@restcontroller
@requestmapping("/api/test")
@slf4j
public class virtualthreadtestcontroller {
@getmapping("/virtual-thread")
public map<string, object> testvirtualthread() {
thread currentthread = thread.currentthread();
map<string, object> result = new hashmap<>();
result.put("threadname", currentthread.getname());
result.put("isvirtual", currentthread.isvirtual());
result.put("threadid", currentthread.threadid());
log.info("请求处理线程: {}, 是否为虚拟线程: {}",
currentthread.getname(), currentthread.isvirtual());
return result;
}
}
1.3 定时任务中使用虚拟线程
配置类改造:
/**
* 定时任务配置(虚拟线程版本)
*
* @author
* @date 2024-10-31
*/
@slf4j
@configuration
@enablescheduling
@conditionalonproperty(name = "spring.threads.virtual.enabled", havingvalue = "true")
public class virtualthreadschedulingconfig implements schedulingconfigurer {
@override
public void configuretasks(scheduledtaskregistrar taskregistrar) {
taskregistrar.setscheduler(taskscheduler());
}
@bean
public executor taskscheduler() {
return executors.newvirtualthreadpertaskexecutor();
}
}
使用示例:
@component
@slf4j
public class scheduledtaskexample {
/**
* 定时任务会自动使用虚拟线程执行器
*/
@scheduled(fixedrate = 5000)
public void scheduledtask() {
log.info("定时任务执行 - 线程: {}, 是否为虚拟线程: {}",
thread.currentthread().getname(),
thread.currentthread().isvirtual());
}
}
方式二:手动创建虚拟线程执行器
2.1 改进现有的virtualthreadutils工具类
/**
* 虚拟线程工具类(改进版)
*
* @author
* @date 2024-10-31
*/
@slf4j
@component
public class virtualthreadutils {
private static final executorservice executor = executors.newvirtualthreadpertaskexecutor();
/**
* 执行虚拟线程任务
*
* @param task 任务
* @return future
*/
public static future<?> exevirtualthread(runnable task) {
return executor.submit(() -> {
try {
log.debug("虚拟线程执行任务 - 线程: {}, 是否为虚拟线程: {}",
thread.currentthread().getname(),
thread.currentthread().isvirtual());
task.run();
} catch (exception e) {
log.error("虚拟线程执行任务异常", e);
throw e;
}
});
}
/**
* 执行虚拟线程任务(带返回值)
*
* @param task 任务
* @param <t> 返回值类型
* @return future
*/
public static <t> future<t> exevirtualthread(java.util.concurrent.callable<t> task) {
return executor.submit(() -> {
try {
log.debug("虚拟线程执行任务 - 线程: {}, 是否为虚拟线程: {}",
thread.currentthread().getname(),
thread.currentthread().isvirtual());
return task.call();
} catch (exception e) {
log.error("虚拟线程执行任务异常", e);
throw e;
}
});
}
@predestroy
public void destroy() {
log.info("关闭虚拟线程执行器");
executor.shutdown();
try {
if (!executor.awaittermination(60, timeunit.seconds)) {
executor.shutdownnow();
if (!executor.awaittermination(60, timeunit.seconds)) {
log.error("虚拟线程执行器未能正常关闭");
}
}
} catch (interruptedexception e) {
executor.shutdownnow();
thread.currentthread().interrupt();
}
}
}
2.2 在completablefuture中使用虚拟线程
@service
@slf4j
public class completablefutureexample {
/**
* 使用虚拟线程执行器执行completablefuture
*/
public completablefuture<list<string>> processdataasync() {
executor virtualexecutor = executors.newvirtualthreadpertaskexecutor();
completablefuture<list<string>> future = completablefuture
.supplyasync(() -> {
log.info("异步任务执行 - 线程: {}, 是否为虚拟线程: {}",
thread.currentthread().getname(),
thread.currentthread().isvirtual());
return fetchdata();
}, virtualexecutor)
.thenapplyasync(data -> {
log.info("处理数据 - 线程: {}, 是否为虚拟线程: {}",
thread.currentthread().getname(),
thread.currentthread().isvirtual());
return processdata(data);
}, virtualexecutor);
return future;
}
private list<string> fetchdata() {
// 模拟i/o操作
try {
thread.sleep(100);
} catch (interruptedexception e) {
thread.currentthread().interrupt();
}
return arrays.aslist("data1", "data2", "data3");
}
private list<string> processdata(list<string> data) {
return data.stream()
.map(string::touppercase)
.collect(collectors.tolist());
}
}
方式三:为特定场景配置虚拟线程执行器
3.1 消息队列处理使用虚拟线程
/**
* rabbitmq虚拟线程配置
*
* @author 往事随风去
* @date 2024-10-31
*/
@slf4j
@configuration
@conditionalonproperty(name = "spring.threads.virtual.enabled", havingvalue = "true")
public class rabbitmqvirtualthreadconfig {
@bean
public simplerabbitlistenercontainerfactory rabbitlistenercontainerfactory(
connectionfactory connectionfactory) {
simplerabbitlistenercontainerfactory factory = new simplerabbitlistenercontainerfactory();
factory.setconnectionfactory(connectionfactory);
// 使用虚拟线程执行器处理消息
factory.settaskexecutor(new taskexecutoradapter(executors.newvirtualthreadpertaskexecutor()));
return factory;
}
}
3.2 数据库操作使用虚拟线程
对于mybatis等数据库操作,通常不需要特别配置,因为:
- 数据库连接池(如druid)会管理连接
- 虚拟线程在执行i/o阻塞操作时会自动释放平台线程
- 可以提高并发查询能力
四、最佳实践
1. 适用场景
虚拟线程适合:
- i/o密集型任务(数据库查询、http请求、文件操作)
- 大量并发请求处理
- 异步任务处理
- web请求处理
虚拟线程不适合:
- cpu密集型任务(计算密集型,应使用平台线程池)
- 需要线程本地存储(threadlocal)的场景(虚拟线程会频繁切换)
- 需要精确控制线程数量的场景
2. 注意事项
2.1 线程本地存储(threadlocal)
虚拟线程会频繁切换,threadlocal的使用需要注意:
// ❌ 不推荐:在虚拟线程中使用threadlocal存储大量数据 threadlocal<list<string>> threadlocal = new threadlocal<>(); // ✅ 推荐:使用scopedvalue(java 21+)或谨慎使用threadlocal scopedvalue<string> scopedvalue = scopedvalue.newinstance();
2.2 线程池大小配置
启用虚拟线程后,不需要配置线程池大小,虚拟线程会自动管理:
# ❌ 不需要配置这些(虚拟线程会自动管理)
server:
tomcat:
threads:
max: 400 # 虚拟线程模式下无效
min-spare: 100 # 虚拟线程模式下无效
2.3 监控和调试
虚拟线程的监控需要使用新的api:
@service
@slf4j
public class virtualthreadmonitor {
@scheduled(fixedrate = 60000)
public void monitorvirtualthreads() {
threadmxbean threadbean = managementfactory.getthreadmxbean();
long[] threadids = threadbean.getallthreadids();
long virtualthreadcount = arrays.stream(threadids)
.maptoobj(id -> {
threadinfo info = threadbean.getthreadinfo(id);
return info != null ? thread.ofplatform().getthreadgroup()
.findthread(id) : null;
})
.filter(objects::nonnull)
.filter(thread::isvirtual)
.count();
log.info("虚拟线程数量: {}", virtualthreadcount);
}
}
3. 性能优化建议
3.1 混合使用平台线程和虚拟线程
@configuration
public class hybridthreadconfig {
/**
* cpu密集型任务使用平台线程池
*/
@bean("cpuintensiveexecutor")
public executor cpuintensiveexecutor() {
int processors = runtime.getruntime().availableprocessors();
return executors.newfixedthreadpool(processors);
}
/**
* i/o密集型任务使用虚拟线程
*/
@bean("iointensiveexecutor")
public executor iointensiveexecutor() {
return executors.newvirtualthreadpertaskexecutor();
}
}
3.2 避免在虚拟线程中执行长时间cpu计算
@service
public class taskservice {
@autowired
@qualifier("cpuintensiveexecutor")
private executor cpuexecutor;
@autowired
@qualifier("iointensiveexecutor")
private executor ioexecutor;
public void processtask() {
// i/o操作使用虚拟线程
completablefuture<string> data = completablefuture
.supplyasync(this::fetchdata, ioexecutor);
// cpu计算使用平台线程
completablefuture<string> result = data
.thenapplyasync(this::heavycomputation, cpuexecutor);
}
}
五、迁移建议
1. 逐步迁移
第一步:启用虚拟线程
spring: threads: virtual: enabled: true第二步:改造异步配置
- 将线程池配置类改造为使用虚拟线程
- 将
scheduledtaskcofiguration改造为使用虚拟线程
第三步:测试和验证
- 验证web请求是否使用虚拟线程
- 验证异步方法是否使用虚拟线程
- 验证定时任务是否使用虚拟线程
第四步:性能测试
- 对比启用虚拟线程前后的性能
- 监控内存使用情况
- 监控线程数量
2. 回滚方案
如果出现问题,可以快速回滚:
spring:
threads:
virtual:
enabled: false # 禁用虚拟线程,恢复传统线程池
六、总结
虚拟线程在spring boot中的正确使用方式:
- 启用方式:配置
spring.threads.virtual.enabled=true - web请求:自动使用虚拟线程处理(无需额外配置)
- 异步方法:通过
@async自动使用虚拟线程 - 定时任务:需要配置
schedulingconfigurer - 手动创建:使用
executors.newvirtualthreadpertaskexecutor() - 注意事项:避免cpu密集型任务,谨慎使用threadlocal
通过合理使用虚拟线程,可以显著提高i/o密集型应用的并发处理能力,减少线程资源消耗。
到此这篇关于虚拟线程在spring boot中的正确使用方式及最佳实践的文章就介绍到这了,更多相关springboot中虚拟线程使用方式内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论