日常业务(Spring Boot Web、微服务、数据库、消息队列、文件导出等),通常会有线程执行 IO 阻塞的业务场景,每个场景都会配Java/Spring Boot 实际代码示例,并说明为什么会阻塞、虚拟线程如何优化:
IO 阻塞:线程发起「输入 / 输出操作」后,必须等待操作完成才能继续执行,期间线程 idle(空闲),平台线程会被操作系统挂起,造成资源浪费。虚拟线程的价值:遇到这些阻塞时,JVM 会自动解绑平台线程,把平台线程分配给其他虚拟线程,极大提升吞吐量。
调用第三方接口、内部微服务接口(RestTemplate/OkHttp 同步调用)
// 线程会阻塞在这里,等待第三方接口返回结果
String result = restTemplate.getForObject("https://api.xxx.com/user", String.class);
阻塞原因:等待网络响应
虚拟线程优化:阻塞时释放平台线程
所有基于 JDBC 同步的数据库查询 / 插入 / 更新(MyBatis、JPA、JdbcTemplate)
// 线程阻塞:等待数据库返回查询结果
List<Order> orders = orderMapper.selectList(queryWrapper);
阻塞原因:等待数据库网络 IO + 磁盘 IO
虚拟线程优化:海量请求下,不再因为数据库阻塞占用大量平台线程
Spring Data Redis 同步操作
// 线程阻塞:等待Redis返回数据
String userInfo = redisTemplate.opsForValue().get("user:1001");
结合你之前问的 Kafka 导出文件 场景:
// 发送消息时的阻塞等待、消费消息的阻塞处理
kafkaTemplate.send("export-task-topic", msg).get();
你之前配置的 MinIO 文件上传、导出文件下载:
// 阻塞:等待文件上传到MinIO完成
minioTemplate.upload(bucketName, fileName, inputStream);
// 阻塞:等待订单服务返回结果
OrderDTO order = orderFeignService.getOrderById(1001);
所有本地文件读写、大文件导出 / 导入(你之前的订单 / 用户导出场景)
// 阻塞:等待磁盘写入/读取完成
Files.write(Paths.get("order.csv"), csvData.getBytes());
// 导出订单时,流式写入文件,全程阻塞IO
try (CSVWriter writer = new CSVWriter(new FileWriter("user_export.csv"))) {
writer.writeAll(userList); // 阻塞操作
}
Thread.sleep()模拟阻塞、延时处理:
// 线程强制休眠,属于显式阻塞
Thread.sleep(1000);
synchronized、ReentrantLock 等待锁释放:
synchronized (this) {
// 线程阻塞:等待其他线程释放锁
}
原生 Java Socket 编程、TCP 通信:
Socket socket = new Socket("127.0.0.1", 8080);
// 阻塞:等待客户端连接/数据传输
InputStream in = socket.getInputStream();
in.read(buffer); // 阻塞IO
只要代码满足下面任意一条,就是 IO 阻塞场景:
Spring Boot 3.2+ 开启虚拟线程后,以下业务收益最大:
你只需要在 application.yml 加一行,无需修改任何业务代码,自动优化所有上述阻塞场景:
spring:
threads:
virtual:
enabled: true # 开启虚拟线程
你日常开发中 90% 的业务代码 都属于 IO 阻塞场景:
虚拟线程就是为这些阻塞场景量身定制的,能让你的服务吞吐量提升 数倍~十几倍。