[Java] Virtual Thread의 이해

슈퍼대디·2024년 12월 23일

CS면접대비

목록 보기
5/13

Java Virtual Thread의 이해

목차

  1. Virtual Thread 도입 배경
  2. Virtual Thread vs Platform Thread
  3. Virtual Thread 구현과 활용
  4. 면접 예상 질문

1. Virtual Thread 도입 배경

기존 동시성 모델의 한계

Java의 전통적인 스레드 모델은 운영체제의 스레드와 1:1로 매핑되는 방식을 사용해왔습니다. 이는 다음과 같은 문제를 야기했습니다:

  1. 확장성 제한

    • 각 스레드가 상당한 메모리를 소비 (약 1MB의 스택 메모리)
    • 스레드 생성과 컨텍스트 스위칭의 높은 비용
    • 동시 처리 가능한 작업 수의 제한
  2. 비동기 프로그래밍의 복잡성

    // 기존 비동기 방식의 복잡한 코드
    CompletableFuture.supplyAsync(() -> findUser(userId))
        .thenApplyAsync(user -> enrichUserData(user))
        .thenApplyAsync(user -> saveUser(user))
        .exceptionally(throwable -> handleError(throwable));

Project Loom의 해결책

Oracle은 이러한 문제를 해결하기 위해 Project Loom을 통해 Virtual Thread를 도입했습니다:

  1. 경량 스레드 구현

    • 최소한의 메모리 사용 (약 1KB)
    • JVM에 의한 효율적인 관리
    • 수십만 개의 동시 작업 처리 가능
  2. 동기 스타일의 프로그래밍 유지

    // Virtual Thread를 사용한 간단한 코드
    Thread.startVirtualThread(() -> {
        User user = findUser(userId);
        user = enrichUserData(user);
        saveUser(user);
    });

2. Virtual Thread vs Platform Thread

구조적 차이

Platform Thread vs Virtual Thread 비교

위 다이어그램은 Platform Thread와 Virtual Thread의 주요 차이점을 보여줍니다:

  • Platform Thread: OS 스레드와 1:1 매핑, 큰 스택 메모리 필요
  • Virtual Thread: JVM에 의해 관리되는 경량 스레드, 최소 메모리 사용

스케줄링 방식

Virtual Thread 스케줄링

Virtual Thread의 스케줄링은 다음과 같이 동작합니다:
1. 여러 Virtual Thread가 소수의 Platform Thread에 매핑
2. JVM 스케줄러가 효율적으로 Thread 관리
3. 블로킹 작업 시 자동으로 다른 Virtual 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());
        }
    }
}

4. 면접 예상 질문

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로도 많은 수의 동시 작업을 효율적으로 처리할 수 있습니다. 이는 마치 동시에 수많은 작업을 처리하는 것처럼 보이지만, 실제로는 적은 수의 시스템 리소스만을 사용합니다.

참고 자료

profile
성장하고싶은 Backend 개발자

0개의 댓글