Java

Java Virtual Thread vs 기존 ThreadPool 구조 비교

마시멜로를찾아서 2025. 6. 19. 10:34
728x90
반응형

Java Virtual Thread vs 기존 ThreadPool 구조를 비교하는 실용적인 예제입니다.
비동기 요청 처리(WebFlux, WebClient, Kafka 등)에서 구조적 차이와 성능/코드 복잡도 비교가 명확히 드러납니다.


⚔️ 목적: 동일한 HTTP 요청 100개를 병렬로 처리

각 요청은 500ms sleep API 호출 시뮬레이션


🔁 1. 기존 ThreadPool (ExecutorService) 방식

ExecutorService executor = Executors.newFixedThreadPool(10);

List<Callable<String>> tasks = IntStream.range(0, 100)
    .mapToObj(i -> (Callable<String>) () -> {
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder(URI.create("https://httpbin.org/delay/0.5")).build();
        return client.send(request, HttpResponse.BodyHandlers.ofString()).body();
    }).toList();

long start = System.currentTimeMillis();
List<Future<String>> futures = executor.invokeAll(tasks);
executor.shutdown();
long elapsed = System.currentTimeMillis() - start;

System.out.println("ThreadPool 총 소요시간: " + elapsed + "ms");

⚠️ 단점:

  • FixedThreadPool 이상으로 쓰레드 수 증가 시 GC 부하, CPU context switching
  • 100개 요청이면 100개 쓰레드 필요 (Blocking 구조)

🚀 2. VirtualThread (Java 21) 방식

ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();

List<Callable<String>> tasks = IntStream.range(0, 100)
    .mapToObj(i -> (Callable<String>) () -> {
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder(URI.create("https://httpbin.org/delay/0.5")).build();
        return client.send(request, HttpResponse.BodyHandlers.ofString()).body();
    }).toList();

long start = System.currentTimeMillis();
List<Future<String>> futures = executor.invokeAll(tasks);
executor.shutdown();
long elapsed = System.currentTimeMillis() - start;

System.out.println("VirtualThread 총 소요시간: " + elapsed + "ms");

✅ 장점:

  • 수천~수만 개의 Virtual Thread 처리 가능
  • 커널 쓰레드와 다르게 Blocking 발생 시 Thread가 반납됨
  • 코드 복잡도 없이 동기 방식처럼 코딩 → 비동기 효과

📊 성능 비교 (100개 요청, delay 500ms 기준)

 

항목 ThreadPool (10개) VirtualThread
실행 시간 약 5~6초 0.6초
사용 스레드 수 10 최대 5~6개 커널 쓰레드
코드 가독성 매우 높음
확장성 제한적 (20~500개 쓰레드가 한계) 수만 개 처리 가능
 

📦 실무 적용 포인트

 

대상 권장 방식
WebFlux + Reactor 기반 그대로 non-blocking 유지
내부 Blocking API + 동기 호출 Virtual Thread 적극 활용
Kafka Consumer 처리 Virtual Thread 가능 (Spring Kafka 3.1 이상)
외부 HTTP 호출 WebClient, 또는 VirtualThread + HttpClient
 

🔧 스프링 설정 팁 (Spring Boot 3.2+)

@Bean
public TaskExecutor applicationTaskExecutor() {
    return TaskExecutors.virtual();
}
728x90
반응형