Java 가상 스레드(Virtual Threads)는 Java 21에서 정식 도입된 경량 스레드로, 수백만 개의 동시 연결을 OS 스레드 없이 처리할 수 있게 한다. Project Loom의 산출물이다.
OS 스레드 vs 가상 스레드
| 항목 | OS 스레드 | 가상 스레드 |
|---|
| 메모리 | ~1MB 스택 | ~수 KB (동적) |
| 생성 비용 | 높음 | 매우 낮음 |
| 최대 개수 | 수천 | 수백만 |
| 블로킹 I/O | 스레드 블로킹 | 가상 스레드만 블로킹 |
| 도입 버전 | 전통적 | Java 21 (LTS) |
기본 사용법
java
// 기존 OS 스레드 방식
Thread osThread = new Thread(() -> {
System.out.println("OS 스레드: " + Thread.currentThread());
});
// 가상 스레드 (Java 21+)
Thread virtualThread = Thread.ofVirtual()
.name("my-virtual-thread")
.start(() -> System.out.println("가상 스레드: " + Thread.currentThread()));
// 가상 스레드 풀 Executor
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 1_000_000; i++) {
executor.submit(() -> {
Thread.sleep(Duration.ofSeconds(1)); // 블로킹 OK
return "완료";
});
}
} // 모든 태스크 완료 후 자동 종료
HTTP 서버 성능 비교
java
// Spring Boot 3.2+: 가상 스레드 활성화
// application.properties
// spring.threads.virtual.enabled=true
// Tomcat이 자동으로 가상 스레드 사용
// 동기 코드 그대로 사용 가능 (reactive 패턴 불필요)
@RestController
class UserController {
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
// 블로킹 DB 호출 → 가상 스레드가 마운트 해제, 다른 작업 처리
return userRepository.findById(id); // 동기 블로킹 OK
}
}
가상 스레드 주의사항
java
// 피해야 할 패턴: synchronized (캐리어 스레드 고정)
synchronized (lock) {
doBlockingIO(); // 가상 스레드 블로킹 시 캐리어 스레드도 블로킹
}
// 권장: ReentrantLock 사용
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
doBlockingIO();
} finally {
lock.unlock();
}
// ThreadLocal 대신 ScopedValue (Java 21+)
ScopedValue<String> USER = ScopedValue.newInstance();
ScopedValue.where(USER, "alice").run(() -> {
System.out.println(USER.get()); // "alice"
});
관련 개념