发布于2026-07-21 阅读(0)
扫一扫,手机访问
来聊聊Spring Boot与Project Loom的集成实践。作为Ja va并发领域近年最值得关注的变化之一,Project Loom引入的虚拟线程,让我们有机会用同步的思维去写异步的代码,这确实是个很有意思的话题。下面直接进入正题。
虚拟线程,说白了就是Project Loom带来的轻量级线程。它由JVM管理,而不是操作系统,所以创建成本极低,内存占用也小得多。
// 创建虚拟线程
Thread virtualThread = Thread.startVirtualThread(() -> {
System.out.println("Running in virtual thread: " + Thread.currentThread());
System.out.println("Is virtual: " + Thread.currentThread().isVirtual());
});
// 虚拟线程池
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
IntStream.range(0, 10000).forEach(i -> {
executor.submit(() -> {
System.out.println("Task " + i + " in " + Thread.currentThread());
try {
Thread.sleep(100); // 虚拟线程中的阻塞操作不会阻塞 OS 线程
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
});
从这张表里能看出,差异确实不小:
| 特性 | 虚拟线程 | 平台线程 |
|---|---|---|
| 创建成本 | 极低 | 高 |
| 内存占用 | 几 KB | 几 MB |
| 数量限制 | 数百万 | 数千 |
| 阻塞行为 | 非阻塞 OS 线程 | 阻塞 OS 线程 |
| 调度 | JVM 调度 | 操作系统调度 |
集成Spring Boot,首先把依赖加上:
org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-webflux test
接下来是配置。核心在于启用虚拟线程支持:
@Configuration
public class VirtualThreadConfig {
@Bean
public ExecutorTaskExecutor virtualThreadTaskExecutor() {
ExecutorTaskExecutor executor = new ExecutorTaskExecutor();
executor.setThreadNamePrefix("virtual-");
executor.setVirtualThreads(true); // 启用虚拟线程
return executor;
}
@Bean
public ApplicationRunner applicationRunner() {
return args -> {
System.out.println("Application started with virtual threads support");
};
}
}
Web服务器层面也需要开启虚拟线程,以Tomcat为例:
server:
port: 8080
tomcat:
threads:
virtual: true # 启用 Tomcat 虚拟线程
jetty:
threads:
virtual: true # 启用 Jetty 虚拟线程
netty:
threads:
virtual: true # 启用 Netty 虚拟线程
现在写个Controller,方法本身是同步的,但在虚拟线程中执行:
@RestController
@RequestMapping("/api")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
// 同步方法,但在虚拟线程中执行
return userService.findById(id);
}
@GetMapping("/users")
public List getUsers() {
// 同步方法,但在虚拟线程中执行
return userService.findAll();
}
}
服务层也一样,数据库操作虽然是阻塞的,但虚拟线程能扛得住:
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 同步方法,但在虚拟线程中执行
public User findById(Long id) {
// 数据库操作(阻塞操作)
return userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException("User not found"));
}
// 同步方法,但在虚拟线程中执行
public List findAll() {
// 数据库操作(阻塞操作)
return userRepository.findAll();
}
// 批量操作
public List batchProcess(List userIds) {
return userIds.stream()
.parallel() // 并行流,使用虚拟线程
.map(this::findById)
.collect(Collectors.toList());
}
}
异步场景下,虚拟线程同样能发挥优势,比如用CompletableFuture:
@Service
public class AsyncService {
private final ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
public CompletableFuture processAsync(String input) {
return CompletableFuture.supplyAsync(() -> {
// 耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "Processed: " + input;
}, executor);
}
public List processBatch(List inputs) {
List> futures = inputs.stream()
.map(this::processAsync)
.toList();
return futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList());
}
}
Spring的@Async注解也能配合虚拟线程使用:
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "virtualThreadExecutor")
public Executor virtualThreadExecutor() {
return Executors.newVirtualThreadPerTaskExecutor();
}
}
@Service
public class AsyncTaskService {
@Async("virtualThreadExecutor")
public CompletableFuture performTask(String input) {
// 耗时操作
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return CompletableFuture.completedFuture("Task completed: " + input);
}
}
JDBC操作是典型的阻塞场景,但虚拟线程能轻松应对:
@Service
public class JdbcUserService {
private final JdbcTemplate jdbcTemplate;
public JdbcUserService(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public List findAll() {
// JDBC 操作(阻塞操作)
return jdbcTemplate.query(
"SELECT * FROM users",
(rs, rowNum) -> User.builder()
.id(rs.getLong("id"))
.name(rs.getString("name"))
.email(rs.getString("email"))
.build()
);
}
public User findById(Long id) {
// JDBC 操作(阻塞操作)
return jdbcTemplate.queryForObject(
"SELECT * FROM users WHERE id = ?",
new Object[]{id},
(rs, rowNum) -> User.builder()
.id(rs.getLong("id"))
.name(rs.getString("name"))
.email(rs.getString("email"))
.build()
);
}
}
JPA同样适用,Repository中的方法会在虚拟线程中执行:
@Repository public interface UserRepository extends JpaRepository{ // 方法会在虚拟线程中执行 List findByNameContaining(String name); Optional findByEmail(String email); } @Service public class JpaUserService { private final UserRepository userRepository; public JpaUserService(UserRepository userRepository) { this.userRepository = userRepository; } @Transactional public User create(User user) { // JPA 操作(阻塞操作) return userRepository.sa ve(user); } @Transactional(readOnly = true) public List findAll() { // JPA 操作(阻塞操作) return userRepository.findAll(); } }
网络请求也是虚拟线程的强项,用RestTemplate举例:
@Service
public class HttpClientService {
private final RestTemplate restTemplate;
public HttpClientService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public String fetchData(String url) {
// HTTP 操作(阻塞操作)
ResponseEntity response = restTemplate.getForEntity(url, String.class);
return response.getBody();
}
public List fetchMultipleUrls(List urls) {
return urls.stream()
.parallel() // 并行流,使用虚拟线程
.map(this::fetchData)
.collect(Collectors.toList());
}
}
WebClient本身是响应式的,但也能在虚拟线程里做阻塞调用:
@Service
public class WebClientService {
private final WebClient webClient;
public WebClientService(WebClient.Builder webClientBuilder) {
this.webClient = webClientBuilder.baseUrl("https://api.example.com").build();
}
// 响应式 API
public Mono fetchDataReactive(String endpoint) {
return webClient.get()
.uri(endpoint)
.retrieve()
.bodyToMono(String.class);
}
// 阻塞式调用(在虚拟线程中)
public String fetchDataBlocking(String endpoint) {
return fetchDataReactive(endpoint).block();
}
}
来看一组直观的对比数据:
| 指标 | 传统线程池 | 虚拟线程 |
|---|---|---|
| 并发数 | 1000 | 100000 |
| 启动时间 | 1-2 秒 | < 1 秒 |
| 内存占用 | 500MB+ | 100MB+ |
| 响应时间 | P99: 100ms+ | P99: 50ms+ |
用基准测试来看实际效果,虚拟线程处理高并发请求的能力让人印象深刻:
@SpringBootTest
public class VirtualThreadBenchmark {
@Autowired
private UserService userService;
@Test
public void testConcurrentRequests() {
int concurrentRequests = 10000;
CountDownLatch latch = new CountDownLatch(concurrentRequests);
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
long startTime = System.currentTimeMillis();
for (int i = 0; i < concurrentRequests; i++) {
executor.submit(() -> {
try {
userService.findById(1L);
} finally {
latch.countDown();
}
});
}
try {
latch.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
long endTime = System.currentTimeMillis();
System.out.println("Time for " + concurrentRequests + " requests: " + (endTime - startTime) + "ms");
}
}
经验表明,以下场景特别适合虚拟线程:
必须警惕的是,虚拟线程并非万能。有几点需要留意:
最后,给一个最佳实践的例子:
// 最佳实践:使用虚拟线程处理 IO 密集型任务
@Service
public class BestPracticeService {
private final ExecutorService virtualExecutor = Executors.newVirtualThreadPerTaskExecutor();
public List fetchDataFromMultipleSources(List sources) {
return sources.stream()
.map(source -> virtualExecutor.submit(() -> fetchFromSource(source)))
.map(future -> {
try {
return future.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
})
.collect(Collectors.toList());
}
private Data fetchFromSource(String source) {
// 模拟网络请求
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return new Data(source, "data");
}
}
Spring Boot与Project Loom的集成,确实为并发编程打开了一扇新的大门。通过虚拟线程,我们能以同步的写法实现异步的效果,在提升并发能力的同时,也降低了编程复杂度。当然,技术选型要因地制宜,最重要的是理解应用的实际需求,这其实可以更优雅一点。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8