Backend

99 Ngày Spring — Ngày 87: Virtual Threads trong Spring Boot

SSite Admin
22 tháng 10, 2026 3 phút đọc 6 lượt xem
99 Ngày Spring — Ngày 87: Virtual Threads trong Spring Boot

Mô hình một luồng cho mỗi request dễ viết nhưng tốn kém khi luồng nền tảng đứng chờ I/O. Virtual thread của Java 21 giữ nguyên kiểu code tuần tự đó, nhưng một luồng đang chờ gần như không tốn tài nguyên. Từ Spring Boot 3.2, bật tính năng này chỉ cần một dòng cấu hình. Phần cần hiểu là giới hạn của hệ thống sẽ chuyển sang chỗ khác.

Virtual Threads trong Spring Boot

1. Bật bằng một thuộc tính

Với spring.threads.virtual.enabled=true trên Java 21, Tomcat và Jetty xử lý mỗi request trên một virtual thread. Executor mặc định cho @Async, bộ lập lịch @Scheduled và các listener của Kafka, RabbitMQ cũng chuyển theo. Code controller và service không đổi một dòng. Nếu ứng dụng không có web server mà chỉ chạy tác vụ định kỳ, đặt thêm spring.main.keep-alive=true vì virtual thread là daemon thread và không giữ JVM sống.

spring.threads.virtual.enabled=true
# only for apps without a web server:
spring.main.keep-alive=true

2. Giới hạn chuyển sang tài nguyên khác

Thread pool của Tomcat trước đây vô tình là cái phanh: tối đa 200 request cùng lúc. Với virtual thread, cái phanh đó biến mất và hàng nghìn request có thể cùng tiến tới database. Giới hạn thật giờ là pool kết nối HikariCP, hạn mức của API đối tác và bộ nhớ. Hãy đặt giới hạn có chủ đích bằng semaphore hoặc bulkhead, và đừng pool virtual thread: chúng được tạo mới cho từng việc.

private final Semaphore partnerLimit = new Semaphore(50);

Quote quote(String sku) throws InterruptedException {
    partnerLimit.acquire();          // cheap to wait on a virtual thread
    try {
        return partnerClient.quote(sku);
    } finally {
        partnerLimit.release();
    }
}

3. Pinning và ThreadLocal

Trên Java 21, một virtual thread chặn bên trong khối synchronized sẽ bị ghim vào luồng nền tảng đang chở nó, làm luồng đó cũng đứng chờ. Nếu nhiều request cùng bị ghim, ứng dụng nghẽn dù CPU rảnh. Chạy với -Djdk.tracePinnedThreads=full hoặc xem sự kiện jdk.VirtualThreadPinned trong JFR để tìm chỗ ghim, rồi thay bằng ReentrantLock hoặc nâng cấp thư viện. Các bản JDK mới hơn đã gỡ phần lớn hạn chế này. ThreadLocal vẫn hoạt động, nhưng dùng nó làm cache cho đối tượng đắt tiền thì mất tác dụng vì mỗi luồng chỉ sống một việc.

  • Việc nặng CPU không nhanh hơn với virtual thread.

  • Kiểm tra driver và thư viện client đang dùng có ghim luồng không trước khi bật ở production.

4. So với WebFlux

Cả hai cùng giải bài toán nhiều request chờ I/O. Virtual thread giữ code tuần tự, stack trace đầy đủ, dùng được JDBC, JPA và mọi thư viện blocking sẵn có. WebFlux cho các operator xử lý luồng dữ liệu và backpressure từ đầu tới cuối, phù hợp streaming. Với phần lớn ứng dụng CRUD gọi database và vài API, MVC cộng virtual thread là con đường ít chi phí nhất.

  • Đo trước và sau với tải đại diện: thông lượng, p99 và số kết nối database.

  • Không trộn: đừng gọi block() của Reactor trên virtual thread như một thói quen.

  • Giữ timeout cho mọi lời gọi ra ngoài; luồng rẻ không làm request treo trở nên vô hại.

Virtual thread gỡ giới hạn về số luồng, nên bạn phải tự đặt giới hạn ở tài nguyên thật. Ngày 88 tối ưu theo hướng khác: khởi động trong mili giây với GraalVM native image.

Tài liệu đối chiếu

  • https://docs.spring.io/spring-boot/reference/features/task-execution-and-scheduling.html

  • https://openjdk.org/jeps/444

S

Site Admin

Engineer and writer. Building things with TypeScript and distributed systems.

Bình luận (0)

Bạn cần đăng nhập bằng Google để bình luận.

Hãy là người bình luận đầu tiên.

Bài viết liên quan

99 Ngày Java — Ngày 87: Gradle

build.gradle.kts, cấu hình phụ thuộc, task và build tăng dần, và so sánh thực tế với Maven.

22 thg 10, 20263 phút6
99 Ngày Spring — Ngày 86: gRPC với Spring

File proto là hợp đồng, stub sinh tự động, status code và deadline, cùng tiêu chí chọn gRPC hay REST.

21 thg 10, 20263 phút5
99 Ngày Java — Ngày 86: Maven

POM và tọa độ, dependency scope, phụ thuộc bắc cầu, vòng đời build và BOM để quản lý phiên bản.

21 thg 10, 20263 phút5