Java의 전통적인 스레드 모델은 운영체제의 스레드와 1:1로 매핑되는 방식을 사용해왔습니다. 이는 다음과 같은 문제를 야기했습니다:
확장성 제한
비동기 프로그래밍의 복잡성
// 기존 비동기 방식의 복잡한 코드
CompletableFuture.supplyAsync(() -> findUser(userId))
.thenApplyAsync(user -> enrichUserData(user))
.thenApplyAsync(user -> saveUser(user))
.exceptionally(throwable -> handleError(throwable));
Oracle은 이러한 문제를 해결하기 위해 Project Loom을 통해 Virtual Thread를 도입했습니다:
경량 스레드 구현
동기 스타일의 프로그래밍 유지
// Virtual Thread를 사용한 간단한 코드
Thread.startVirtualThread(() -> {
User user = findUser(userId);
user = enrichUserData(user);
saveUser(user);
});
위 다이어그램은 Platform Thread와 Virtual Thread의 주요 차이점을 보여줍니다:
Virtual Thread의 스케줄링은 다음과 같이 동작합니다:
1. 여러 Virtual Thread가 소수의 Platform Thread에 매핑
2. JVM 스케줄러가 효율적으로 Thread 관리
3. 블로킹 작업 시 자동으로 다른 Virtual Thread로 전환
// 방법 1: 직접 생성
Thread vThread = Thread.ofVirtual().start(() -> {
// 작업 수행
});
// 방법 2: Executor 사용
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> {
// 작업 수행
return result;
});
}
@Service
public class UserService {
public List<User> processUsers(List<String> userIds) {
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
return userIds.stream()
.map(id -> executor.submit(() -> processUser(id)))
.map(future -> {
try {
return future.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
})
.collect(Collectors.toList());
}
}
}
Q: Virtual Thread와 Platform Thread의 주요 차이점은 무엇인가요?
A: Virtual Thread는 JVM에서 관리되는 경량 스레드로, Platform Thread와는 다음과 같은 차이가 있습니다. 우선 메모리 사용량 측면에서 Platform Thread가 약 1MB의 스택 메모리를 사용하는 반면, Virtual Thread는 약 1KB만을 사용합니다. 또한 스케줄링 방식에서도 차이가 있는데, Platform Thread는 OS에 의해 직접 스케줄링되지만, Virtual Thread는 JVM에 의해 관리되며 소수의 Platform Thread에 매핑되어 동작합니다. 이러한 특성으로 인해 Virtual Thread는 I/O 작업이 많은 애플리케이션에서 특히 효과적입니다.
Q: Virtual Thread는 어떤 상황에서 사용하면 좋을까요?
A: Virtual Thread는 주로 I/O 작업이 많은 상황에서 가장 큰 효과를 발휘합니다. 예를 들어, 마이크로서비스 아키텍처에서 여러 서비스 간의 API 호출이 필요한 경우, 데이터베이스 조회가 빈번한 경우, 또는 파일 시스템 작업이 많은 경우에 적합합니다. 반면, CPU 집약적인 작업이나 복잡한 수학 연산이 필요한 경우에는 Platform Thread를 사용하는 것이 더 적절할 수 있습니다. 특히 동시에 많은 클라이언트 요청을 처리해야 하는 웹 서버에서 Virtual Thread를 사용하면 시스템 리소스를 효율적으로 활용할 수 있습니다.
Q: Virtual Thread의 동작 방식에 대해 설명해주세요.
A: Virtual Thread는 Carrier Thread(Platform Thread)에 의해 실행되며, 다음과 같이 동작합니다. Virtual Thread가 I/O 작업과 같은 블로킹 작업을 만나면, JVM은 해당 Virtual Thread를 자동으로 Carrier Thread에서 분리하고, 다른 Virtual Thread를 실행합니다. 블로킹 작업이 완료되면, Virtual Thread는 다시 사용 가능한 Carrier Thread에 매핑되어 실행을 재개합니다. 이러한 방식으로 적은 수의 Platform Thread로도 많은 수의 동시 작업을 효율적으로 처리할 수 있습니다. 이는 마치 동시에 수많은 작업을 처리하는 것처럼 보이지만, 실제로는 적은 수의 시스템 리소스만을 사용합니다.