-
Java 21 버추얼 스레드 원리와 Spring Boot 설정 방법Server/Spring Boot 2026. 1. 5. 16:39728x90반응형
1. 버추얼 스레드란?
Java 21에서 정식 도입된 경량 스레드입니다. 기존 플랫폼 스레드와 달리 JVM이 직접 관리하며, 메모리 사용량이 적고 수십만 개까지 생성할 수 있습니다.
플랫폼 스레드 vs 버추얼 스레드
구분 플랫폼 스레드 버추얼 스레드 메모리 1MB+ 수 KB 동시 개수 수백 개 수십만 개 관리 주체 OS JVM 스위칭 비용 높음 낮음
2. 버추얼 스레드 동작 원리
구조
버추얼 스레드는 단독으로 실행되지 않습니다. 캐리어 스레드(플랫폼 스레드) 위에서 실행됩니다.
버추얼 스레드 (수만 개 생성 가능) ┌──┐┌──┐┌──┐┌──┐┌──┐┌──┐┌──┐┌──┐... └──┘└──┘└──┘└──┘└──┘└──┘└──┘└──┘ ↓ JVM 스케줄링 캐리어 스레드 (기본값: CPU 코어 수) ┌─────┐┌─────┐┌─────┐┌─────┐ │ C1 ││ C2 ││ C3 ││ C4 │ └─────┘└─────┘└─────┘└─────┘ ↓ OS 스레드 ↓ CPU 코어마운트와 언마운트
버추얼 스레드 전환을 마운트/언마운트라고 부릅니다.
[마운트] : 버추얼 스레드가 캐리어 스레드에 올라타서 실행 [언마운트] : 버추얼 스레드가 캐리어 스레드에서 분리동작 흐름:
버추얼 스레드 A: 실행 중 → DB 대기 → [언마운트] ↓ 캐리어 스레드: A 실행 → 비어있음 → B 실행 ↑ 버추얼 스레드 B: 대기 중 → [마운트]I/O 작업(DB 조회, API 호출 등)으로 대기 상태가 되면 자동으로 언마운트되고, 다른 버추얼 스레드가 마운트됩니다.
OS 컨텍스트 스위칭 vs 마운트/언마운트
구분 OS 컨텍스트 스위칭 마운트/언마운트 발생 위치 커널 JVM (유저 모드) 대상 플랫폼 스레드 간 버추얼 스레드 간 비용 무거움 (수천 CPU 사이클) 가벼움 (수백 CPU 사이클) 저장 범위 전체 CPU 상태 스택만 핵심 정리
버추얼 스레드는 수만 개 생성해도, 실제 OS 컨텍스트 스위칭은 코어 수 정도의 캐리어 스레드 사이에서만 일어나고, 나머지는 JVM 레벨의 가벼운 마운트/언마운트로 처리되어 효율적이다.
3. Spring Boot 버추얼 스레드 설정
요구사항
- Java 21 이상
- Spring Boot 3.2 이상
주의: 두 가지 설정이 별개입니다
┌─────────────────────────────────────────────────┐ │ HTTP 요청 │ └─────────────────────┬───────────────────────────┘ ↓ ┌─────────────────────────────────────────────────┐ │ 톰캣 스레드 │ │ → spring.threads.virtual.enabled 설정 │ └─────────────────────┬───────────────────────────┘ ↓ ┌─────────────────────────────────────────────────┐ │ Controller → Service → Repository │ │ ↓ │ │ @Async 메서드 호출 │ │ ↓ │ │ Executor Bean │ │ → 별도 설정 필요 │ └─────────────────────────────────────────────────┘설정 1: 톰캣 요청 처리용
HTTP 요청을 처리하는 톰캣 스레드에 적용됩니다.
# application.yml spring: threads: virtual: enabled: true설정 2: @Async 비동기 작업용
@Async 어노테이션을 사용하는 비동기 작업에 적용됩니다.
@Configuration @EnableAsync public class AsyncConfig { @Bean public Executor taskExecutor() { return Executors.newVirtualThreadPerTaskExecutor(); } }플랫폼 스레드 풀 vs 버추얼 스레드 Executor
플랫폼 스레드 (기존 방식)
@Bean public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(10); // 기본 스레드 수 executor.setMaxPoolSize(50); // 최대 스레드 수 executor.setQueueCapacity(100); // 대기열 크기 return executor; }- 스레드가 무거워서 풀로 관리
- 개수 제한 필수
버추얼 스레드
@Bean public Executor taskExecutor() { return Executors.newVirtualThreadPerTaskExecutor(); }- 작업마다 새로 생성
- 풀링하지 않음
- 개수 제한 없음
캐리어 스레드 설정 (선택사항)
보통 기본값으로 충분하지만, 필요시 JVM 옵션으로 조정할 수 있습니다.
# 캐리어 스레드 수 (기본값: CPU 코어 수) -Djdk.virtualThreadScheduler.parallelism=8 # 최대 캐리어 스레드 수 -Djdk.virtualThreadScheduler.maxPoolSize=256
4. 전체 설정 예시
build.gradle
plugins { id 'java' id 'org.springframework.boot' version '3.2.0' } java { toolchain { languageVersion = JavaLanguageVersion.of(21) } }application.yml
spring: threads: virtual: enabled: trueAsyncConfig.java
@Configuration @EnableAsync public class AsyncConfig { @Bean public Executor taskExecutor() { return Executors.newVirtualThreadPerTaskExecutor(); } }이렇게 설정하면 톰캣 요청 처리와 @Async 비동기 작업 모두 버추얼 스레드로 동작합니다.
5. 정리
설정 적용 범위 방법 spring.threads.virtual.enabled 톰캣 HTTP 요청 application.yml Executor Bean @Async 비동기 작업 Java Config 기존 코드 변경 없이 설정만으로 버추얼 스레드를 적용할 수 있습니다. WebFlux처럼 코드 스타일을 바꿀 필요 없이 성능 향상을 기대할 수 있다는 점이 가장 큰 장점입니다.
728x90반응형'Server > Spring Boot' 카테고리의 다른 글
Spring Boot 3.x에서 @Async와 버추얼 스레드 조합하기 (0) 2026.01.09 🏦 Spring Boot 대용량 트래픽 환경에서의 계좌 API 구현 (0) 2026.01.05 Spring Boot에서 많이 사용되는 View 엔진 추천 및 장단점 (0) 2025.04.29 자주 사용되는 디자인 패턴에 대해 알아보자 (springboot) (3) 2025.04.28 [Springboot] 인증(Authentication)과 인가(Authorization) 구분하기! (0) 2025.04.21