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 ThreadMount/Unmount 메커니즘에 있습니다.

  • Virtual Thread: 사용자가 작성한 코드가 실행되는 논리적 스레드
  • Carrier Thread: 실제 Platform Thread로 Virtual Thread를 실행
  • Scheduler: ForkJoinPool 기반 스케줄러가 Virtual Thread를 Carrier Thread에 배분

동작 과정:

  1. Mount: Virtual Thread가 Carrier Thread에 탑재되어 실행
  2. Blocking/I/O: I/O 대기 발생 시 JVM이 감지
  3. Unmount: Virtual Thread 상태가 Heap의 Continuation 객체에 저장
  4. 재실행: 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는 빠른 스레드가 아닌 효율적인 스레드입니다.
반응형