Contents
see List개요
Java 21에서 정식 도입된 Virtual Thread(가상 스레드)는 Java 동시성 프로그래밍의 패러다임을 완전히 바꾸고 있습니다. 기존 플랫폼 스레드(OS 스레드)의 무거운 비용 문제를 해결하며, 수백만 개의 동시 작업을 손쉽게 처리할 수 있게 합니다. 이 글에서는 Virtual Thread의 동작 원리와 실전 활용 패턴을 상세히 다룹니다.
핵심 개념
Virtual Thread는 JVM이 관리하는 경량 스레드로, OS 스레드 위에서 다중화(multiplexing)됩니다. 핵심 특성은 다음과 같습니다.
- 경량성: 플랫폼 스레드가 약 1MB 스택 메모리를 차지하는 반면, Virtual Thread는 수 KB에 불과
- 자동 스케줄링: ForkJoinPool 기반의 캐리어 스레드에서 자동으로 마운트/언마운트
- 블로킹 투명성: I/O 블로킹 시 캐리어 스레드를 자동으로 해제하여 다른 Virtual Thread가 사용
- 기존 API 호환: Thread, ExecutorService 등 기존 API를 그대로 사용 가능
- 구조적 동시성: StructuredTaskScope를 통한 안전한 동시 작업 관리
실전 예제
기본적인 Virtual Thread 생성과 ExecutorService 활용 예제입니다.
// 기본 Virtual Thread 생성
Thread vThread = Thread.ofVirtual()
.name("worker-", 0)
.start(() -> {
System.out.println("Virtual Thread: " + Thread.currentThread());
});
// ExecutorService와 함께 사용
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
List<Future<String>> futures = new ArrayList<>();
for (int i = 0; i < 100_000; i++) {
final int taskId = i;
futures.add(executor.submit(() -> {
Thread.sleep(Duration.ofSeconds(1));
return "Task " + taskId + " 완료";
}));
}
// 10만 개의 동시 작업이 가상 스레드로 처리됨
for (Future<String> future : futures) {
future.get(); // 결과 수집
}
}
StructuredTaskScope를 활용한 구조적 동시성 예제입니다.
// 구조적 동시성: 여러 API를 병렬 호출
public record UserProfile(String name, List<Order> orders, int points) {}
public UserProfile fetchUserProfile(long userId) throws Exception {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
// 세 API를 병렬로 호출
Subtask<String> nameTask = scope.fork(() -> fetchUserName(userId));
Subtask<List<Order>> ordersTask = scope.fork(() -> fetchOrders(userId));
Subtask<Integer> pointsTask = scope.fork(() -> fetchPoints(userId));
scope.join(); // 모든 작업 대기
scope.throwIfFailed(); // 하나라도 실패 시 예외
return new UserProfile(
nameTask.get(),
ordersTask.get(),
pointsTask.get()
);
}
}
// Spring Boot 3.2+에서 Virtual Thread 활성화
// application.yml
// spring:
// threads:
// virtual:
// enabled: true
활용 팁
- Virtual Thread는 I/O 바운드 작업에 최적화되어 있으며, CPU 바운드 작업에는 기존 플랫폼 스레드가 더 적합합니다.
synchronized블록 내에서 블로킹 I/O를 수행하면 캐리어 스레드가 고정(pinning)되므로,ReentrantLock으로 대체하는 것이 좋습니다.- ThreadLocal 사용을 최소화하세요. Virtual Thread가 수백만 개 생성될 경우 메모리 누수의 원인이 됩니다.
- Spring Boot 3.2 이상에서는 설정 한 줄로 모든 요청 처리를 Virtual Thread로 전환할 수 있습니다.
- 모니터링 시
jcmd의 Thread dump에서 Virtual Thread도 확인 가능하며, JFR(Java Flight Recorder)로 성능을 추적하세요.
마무리
Virtual Thread는 Java 서버 애플리케이션의 처리량을 극적으로 향상시킬 수 있는 혁신적인 기능입니다. 기존 thread-per-request 모델을 유지하면서도 리액티브 프로그래밍 수준의 확장성을 얻을 수 있어, 복잡한 리액티브 코드를 작성할 필요가 없어집니다. Spring Boot, Quarkus 등 주요 프레임워크가 이미 지원하고 있으므로, 실무 도입을 적극 검토해볼 시점입니다.
java
| No | 작성일 | Title |
|---|---|---|
| 2874 | 2026. 05. 24. | Java 서비스 ThreadPoolExecutor 포화 방지: 큐 크기, 거절 정책, 백프레셔 실전 가이드 |
| 2799 | 2026. 05. 16. | Java 운영 장애 분석을 위한 JFR·jcmd 실전 가이드: 재시작 없이 CPU·메모리 증거 남기기 |
| 2603 | 2026. 04. 24. | Java 26 Pattern Matching 실전 가이드: Records · Sealed Classes · Primitive Patterns로 분기 없는 도메인 모델링 |
| 2518 | 2026. 04. 16. | Java 26 Virtual Threads & Structured Concurrency 실전 가이드: 가상 스레드로 고성능 서버 구축하기 |
| 2491 | 2026. 04. 14. | Java 21/25 Virtual Threads 완벽 가이드: 기존 스레드 모델과의 차이, 실전 활용법 |
| 2470 | 2026. 04. 13. | Java 25 Virtual Threads & Structured Concurrency 완벽 가이드 |
| 2450 | 2026. 04. 12. | Java 21 Virtual Threads 완벽 가이드: Project Loom으로 수백만 스레드 다루기 |
| 2426 | 2026. 04. 11. | Java 24 Primitive Type Pattern Matching 완벽 가이드 - switch와 instanceof 확장 |
| 2390 | 2026. 04. 09. | Java 26 신기능 완벽 가이드 - AOT 캐시, Compact Headers, 패턴 매칭 실전 활용 |
| 2369 | 2026. 04. 08. | Java 21/25 완벽 가이드: Virtual Threads, Record Patterns, Structured Concurrency 실전 적용 |