IT/Java
Java Virtual Threads (Project Loom) 입문 및 성능 튜토리얼
어느 개발자의 블로그
2025. 11. 11. 14:50
반응형
Java Virtual Threads (Project Loom) 입문 및 성능 튜토리얼
1. Virtual Threads란?
Virtual Threads는 Java 21에서 정식으로 도입된 경량화된 스레드로, Project Loom의 결과물입니다. 기존의 Platform Thread(OS 스레드와 1:1로 매핑되는 스레드)와는 다르게, Virtual Thread는 JVM 내부에서 관리되는 사용자 모드 스레드입니다.
핵심 특징:
- 메모리 효율: Platform Thread는 약 1~2MB의 메모리를 사용하는 반면, Virtual Thread는 약 10~20KB만 사용합니다.
- 생성 속도: Platform Thread의 10만 개 생성에 4,005ms가 소요되지만, Virtual Thread는 166ms만 필요합니다 (약 20배 빠름).
- 경량성: OS 스레드와의 연결이 없어 수백만 개의 Virtual Thread를 생성할 수 있습니다.
2. Virtual Threads와 Platform Threads의 비교
Virtual Thread의 가장 큰 차별점은 Platform Thread와의 관계입니다.
| 특성 | Platform Thread | Virtual Thread |
|---|---|---|
| OS 스레드 연결 | 1:1 매핑 | 없음 (JVM 관리) |
| 메모리 사용 | 1~2MB | 10~20KB |
| 생성 비용 | 높음 (시스템 콜) | 낮음 |
| 최대 개수 | OS 제한 (수천 개) | JVM Heap 허용 한도 내 |
| 컨텍스트 스위칭 | 커널 수준 | JVM 수준 (빠름) |
| 적합한 작업 | CPU 집약적 | I/O 중심 |
3. Virtual Threads의 내부 동작 원리
Virtual Threads의 성능 향상 비결은 Carrier Thread와 Mount/Unmount 메커니즘에 있습니다.
- Virtual Thread: 사용자가 작성한 코드가 실행되는 논리적 스레드
- Carrier Thread: 실제 Platform Thread로 Virtual Thread를 실행
- Scheduler: ForkJoinPool 기반 스케줄러가 Virtual Thread를 Carrier Thread에 배분
동작 과정:
- Mount: Virtual Thread가 Carrier Thread에 탑재되어 실행
- Blocking/I/O: I/O 대기 발생 시 JVM이 감지
- Unmount: Virtual Thread 상태가 Heap의 Continuation 객체에 저장
- 재실행: I/O 완료 시 재마운트되어 이어서 실행
이 과정에서 컨텍스트 스위칭 비용이 커널 → JVM 수준으로 낮아지며, 성능이 크게 향상됩니다.
4. 성능 비교 및 벤치마크
10,000개 HTTP 요청 처리 (I/O 대기 300ms):
- Virtual Thread: 437ms 평균 완료
- Platform Thread (200개): 15,549ms
- Platform Thread (1000개): 3,138ms
K6 부하 테스트 결과:
| 메트릭 | Virtual Thread | Traditional Thread |
|---|---|---|
| 처리량 | 14,063 iterations (33.45 req/s) | 7,312 iterations (17.38 req/s) |
| 성능 향상 | - | 약 92% 증가 |
| 평균 응답시간 | 405.66ms | 966.61ms |
| p95 응답시간 | 549.59ms | 1.53s |
→ Virtual Thread가 약 2배의 처리량을 보여줍니다.
5. Virtual Threads 사용 방법
5.1 기본 생성
// 1. 간단한 실행
Thread.startVirtualThread(() -> System.out.println("Virtual Thread 실행"));
// 2. 빌더 사용
Thread.ofVirtual().name("my-vt").start(() -> System.out.println("이름 지정 Virtual Thread"));
// 3. ExecutorService
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 100; i++) executor.submit(() -> { /* 작업 */ });
}
5.2 Spring Boot 3.2 이상 설정
spring:
threads:
virtual:
enabled: true
5.3 Spring Boot 3.2 미만 수동 설정
@Configuration
public class VirtualThreadConfig {
@Bean
public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
return protocolHandler -> protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
}
@Bean(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME)
public AsyncTaskExecutor applicationTaskExecutor() {
return new TaskExecutorAdapter(Executors.newVirtualThreadPerTaskExecutor());
}
}
6. Virtual Threads의 제약사항과 주의점
6.1 CPU Bound 작업에는 비효율적
Virtual threads are not faster threads; they exist to provide scale, not speed.
6.2 Pinning 문제
원인: synchronized, Object.wait(), JNI 호출 등
해결: synchronized → ReentrantLock 전환, 최신 JDBC 사용
java -Djdk.tracePinnedThreads=full YourApp
6.3 ThreadLocal 제한
// 권장하지 않음
ThreadLocal<Connection> conn = new ThreadLocal<>();
// 권장 방식
Map<String, Connection> cache = Collections.synchronizedMap(new HashMap<>());
6.4 데몬 스레드 주의
spring:
threads:
virtual:
enabled: true
main:
keep-alive: true
6.5 리소스 제한
Semaphore semaphore = new Semaphore(20);
executor.submit(() -> {
try {
semaphore.acquire();
// DB 작업
} finally {
semaphore.release();
}
});
7. Virtual Threads 최적화 팁
-Djdk.virtualThreadScheduler.parallelism=10
-Djdk.virtualThreadScheduler.maxPoolSize=256
-Djdk.virtualThreadScheduler.minRunnable=1
모니터링
jps
jcmd <PID> Thread.dump_to_file -format=text dump.txt
8. Virtual Threads vs WebFlux 비교
| 특성 | Virtual Thread | WebFlux |
|---|---|---|
| 학습 곡선 | 낮음 | 높음 |
| 코드 복잡도 | 낮음 | 높음 |
| 스택 트레이스 | 명확 | 파편화 |
| 성능 | ~5,200 req/s | ~5,280 req/s |
| 라이브러리 지원 | Spring Data JPA | R2DBC |
| 디버깅 | 쉬움 | 어려움 |
결론: Virtual Thread는 동기 스타일을 유지하며 Reactive 수준의 성능을 제공합니다.
9. Java 버전별 주의사항
- Java 21: Virtual Thread 정식 도입, Pinning 일부 존재
- Java 24: JEP 491로 synchronized 내 Unmount 가능, 문제 해결
- 권장: JDBC 사용 많다면 Java 24 이상
결론
Virtual Threads는 Java 동시성의 새로운 패러다임입니다. I/O 중심 앱에서 Reactive 수준의 처리량을 제공하면서도 동기 코드의 단순성을 유지합니다. 다만 Pinning, ThreadLocal, 리소스 관리 등의 제약을 인지하고 적용해야 합니다.
시작하기 좋은 조건:
- Spring Boot 3.2+ 사용
- I/O 중심 애플리케이션
- Java 24 이상 (Pinning 문제 해결)
핵심: Virtual Thread는 빠른 스레드가 아닌 효율적인 스레드입니다.
반응형