Virtaul Thread 알아보기

희운·2026년 2월 25일

Virtual Thread 톺아보기

Java 21에서 도입된 Virtual Thread의 구조와 동작 원리를 이해하기 쉽게 정리한 문서입니다.

목차

  1. 먼저 알아야 할 용어
  2. 기존 Thread 모델의 문제점
  3. Virtual Thread 구조
  4. Virtual Thread 동작 원리
  5. park/unpark 동작 상세
  6. 기존 Thread 모델과의 호환성
  7. 핵심 요약

1. 먼저 알아야 할 용어

1.1 Platform Thread (플랫폼 스레드)

┌─────────────────────────────────────────────────────────────────┐
│  Platform Thread = 기존의 Java Thread                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  • OS 스레드와 1:1로 매핑되는 "무거운" 스레드                   │
│  • 생성 비용: 약 1MB 스택 메모리                                │
│  • OS 커널이 스케줄링 담당                                      │
│  • 컨텍스트 스위칭 = OS 레벨에서 발생 (비용 높음)               │
│                                                                 │
│  ┌─────────────┐         ┌─────────────┐                        │
│  │ Java Thread │ ──1:1── │ OS Thread   │                        │
│  └─────────────┘         └─────────────┘                        │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

1.2 Virtual Thread (가상 스레드)

┌─────────────────────────────────────────────────────────────────┐
│  Virtual Thread = JVM이 관리하는 "경량" 스레드                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  • OS 스레드와 직접 매핑되지 않음                               │
│  • 생성 비용: 수백 바이트 ~ 수 KB                               │
│  • JVM이 스케줄링 담당                                          │
│  • 컨텍스트 스위칭 = JVM 레벨에서 발생 (비용 낮음)              │
│                                                                 │
│  ┌─────────────┐                                                │
│  │ Virtual     │ ──┐                                            │
│  │ Thread 1    │   │      ┌─────────────┐     ┌─────────────┐   │
│  ├─────────────┤   ├──N:1─│ Platform    │─1:1─│ OS Thread   │   │
│  │ Virtual     │   │      │ Thread      │     └─────────────┘   │
│  │ Thread 2    │ ──┤      └─────────────┘                       │
│  ├─────────────┤   │       (Carrier)                            │
│  │ Virtual     │   │                                            │
│  │ Thread 3    │ ──┘                                            │
│  └─────────────┘                                                │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

1.3 Carrier Thread (캐리어 스레드)

┌─────────────────────────────────────────────────────────────────┐
│  Carrier Thread = Virtual Thread를 "태워서" 실행하는 스레드     │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  • 실제로는 Platform Thread                                     │
│  • Virtual Thread의 작업을 대신 실행해줌                        │
│  • "캐리어(운반자)"라는 이름의 의미:                            │
│    - Virtual Thread를 "태워서" CPU까지 운반                     │
│    - 실제 OS 스레드 위에서 실행되게 해줌                        │
│                                                                 │
│  비유:                                                          │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  Virtual Thread = 승객 (가볍고 많을 수 있음)                ││
│  │  Carrier Thread = 버스 (무겁지만 승객을 태워서 이동)        ││
│  │  OS Thread = 도로 (실제 이동이 일어나는 곳)                 ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

1.4 ForkJoinPool (스케줄러)

┌─────────────────────────────────────────────────────────────────┐
│  ForkJoinPool = Virtual Thread들의 스케줄러                     │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  역할:                                                          │
│  • Carrier Thread Pool을 관리                                   │
│  • Virtual Thread 작업을 Carrier Thread에 분배                  │
│  • Work Stealing 알고리즘으로 부하 분산                         │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │                    ForkJoinPool                             ││
│  │  ┌──────────────┐ ┌──────────────┐ ┌──────────────┐         ││
│  │  │ Carrier      │ │ Carrier      │ │ Carrier      │         ││
│  │  │ Thread 1     │ │ Thread 2     │ │ Thread 3     │         ││
│  │  │ [workQueue]  │ │ [workQueue]  │ │ [workQueue]  │         ││
│  │  └──────────────┘ └──────────────┘ └──────────────┘         ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

1.5 Work Queue (작업 큐)

┌─────────────────────────────────────────────────────────────────┐
│  Work Queue = 각 Carrier Thread가 가진 작업 대기열              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  • 각 Carrier Thread마다 하나씩 보유                            │
│  • Virtual Thread의 작업(runContinuation)들이 대기              │
│  • Deque(양방향 큐) 구조                                        │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  Carrier Thread의 Work Queue                                ││
│  │                                                             ││
│  │  ┌────────┬────────┬────────┬────────┐                      ││
│  │  │ Task 1 │ Task 2 │ Task 3 │ Task 4 │ ← push (새 작업)     ││
│  │  └────────┴────────┴────────┴────────┘                      ││
│  │      ↑                                                      ││
│  │      pop (실행할 작업)                                      ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

1.6 runContinuation (실행 컨티뉴에이션)

┌─────────────────────────────────────────────────────────────────┐
│  runContinuation = Virtual Thread의 "실제 작업 내용"            │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  • Virtual Thread가 실행해야 할 코드 (Runnable)                 │
│  • 중단점(suspension point)에서 상태 저장 가능                  │
│  • 재개 시 저장된 지점부터 계속 실행                            │
│                                                                 │
│  비유:                                                          │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  runContinuation = 책갈피가 있는 책                         ││
│  │                                                             ││
│  │  • 읽다가 중단 → 책갈피 끼움 (상태 저장)                    ││
│  │  • 나중에 재개 → 책갈피 위치부터 계속 (상태 복원)           ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

1.7 Work Stealing (작업 훔치기)

┌─────────────────────────────────────────────────────────────────┐
│  Work Stealing = 유휴 스레드가 바쁜 스레드의 작업을 가져옴      │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  상황: Carrier-1은 바쁘고, Carrier-3은 할 일이 없음             │
│                                                                 │
│  Carrier-1: [Task1, Task2, Task3, Task4]  ← 작업 많음           │
│  Carrier-2: [Task5, Task6]                                      │
│  Carrier-3: []                            ← 유휴 상태           │
│                                                                 │
│  Work Stealing 발생:                                            │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  Carrier-3이 Carrier-1의 큐 뒤쪽에서 Task4를 "훔쳐옴"       ││
│  │                                                             ││
│  │  Carrier-1: [Task1, Task2, Task3]                           ││
│  │  Carrier-3: [Task4] ← 훔쳐온 작업 실행                      ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                 │
│  효과: 모든 Carrier Thread가 균등하게 일함 → 부하 분산          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

1.8 park / unpark

┌─────────────────────────────────────────────────────────────────┐
│  park = 스레드를 일시 정지 (대기 상태로 전환)                   │
│  unpark = 스레드를 깨움 (실행 가능 상태로 전환)                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Platform Thread의 park/unpark:                                 │
│  • OS 커널에 요청 → OS가 스레드 스케줄링                        │
│  • 비용 높음 (시스템 콜 필요)                                   │
│                                                                 │
│  Virtual Thread의 park/unpark:                                  │
│  • JVM 내부에서 처리 → OS 개입 없음                             │
│  • 비용 낮음 (단순히 Work Queue에서 제거/추가)                  │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  park (대기):                                               ││
│  │  • Virtual Thread를 Carrier에서 분리 (unmount)              ││
│  │  • 상태를 힙 메모리에 저장                                  ││
│  │  • Carrier Thread는 다른 Virtual Thread 실행                ││
│  │                                                             ││
│  │  unpark (깨움):                                             ││
│  │  • 힙 메모리에서 상태 복원                                  ││
│  │  • Work Queue에 다시 추가                                   ││
│  │  • Carrier Thread에 재할당 (mount)                          ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

1.9 mount / unmount

┌─────────────────────────────────────────────────────────────────┐
│  mount = Virtual Thread가 Carrier Thread에 "탑승"              │
│  unmount = Virtual Thread가 Carrier Thread에서 "하차"          │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  mount (탑승):                                                  │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  Virtual Thread가 Carrier Thread 위에서 실행 시작           ││
│  │                                                             ││
│  │  ┌──────────────┐                                           ││
│  │  │ Virtual      │ ──mount──▶ ┌──────────────┐               ││
│  │  │ Thread       │            │ Carrier      │               ││
│  │  │ (힙 메모리)  │            │ Thread       │               ││
│  │  └──────────────┘            │ (실행 중)    │               ││
│  │                              └──────────────┘               ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                 │
│  unmount (하차):                                                │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  I/O 대기 등으로 Virtual Thread가 Carrier에서 분리          ││
│  │                                                             ││
│  │  ┌──────────────┐            ┌──────────────┐               ││
│  │  │ Virtual      │ ◀──unmount─│ Carrier      │               ││
│  │  │ Thread       │            │ Thread       │               ││
│  │  │ (힙에 저장)  │            │ (다른 VT 실행)│              ││
│  │  └──────────────┘            └──────────────┘               ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

2. 기존 Thread 모델의 문제점

┌─────────────────────────────────────────────────────────────────┐
│  Platform Thread의 한계                                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  1. 생성 비용이 높음                                            │
│     • 스레드당 약 1MB 스택 메모리                               │
│     • 1000개 스레드 = 1GB 메모리                                │
│                                                                 │
│  2. OS 스레드와 1:1 매핑                                        │
│     • OS 스레드 수에 제한 있음                                  │
│     • 수천 개 이상 생성 어려움                                  │
│                                                                 │
│  3. Context Switching 비용 높음                                 │
│     • OS 커널 개입 필요 (시스템 콜)                             │
│     • 레지스터 저장/복원, 캐시 무효화                           │
│                                                                 │
│  4. I/O 대기 시 비효율                                          │
│     ┌─────────────────────────────────────────────────────────┐ │
│     │  Platform Thread가 I/O 대기 시:                         │ │
│     │  • OS 스레드가 블로킹됨                                 │ │
│     │  • 아무것도 안 하면서 리소스 점유                       │ │
│     │  • 다른 작업을 처리할 수 없음                           │ │
│     └─────────────────────────────────────────────────────────┘ │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

3. Virtual Thread 구조

3.1 Virtual Thread 내부 구성요소

┌─────────────────────────────────────────────────────────────────┐
│  VirtualThread 객체의 내부 구조                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  VirtualThread {                                                │
│      │                                                          │
│      ├── carrierThread ──────────────────────────────────────┐  │
│      │   • 실제 작업을 수행하는 Platform Thread              │  │
│      │   • Virtual Thread가 mount될 대상                     │  │
│      │   • workQueue를 보유                                  │  │
│      │                                                       │  │
│      ├── scheduler (ForkJoinPool) ───────────────────────────┤  │
│      │   • Carrier Thread Pool 관리                          │  │
│      │   • Virtual Thread 작업 스케줄링                      │  │
│      │   • Work Stealing으로 부하 분산                       │  │
│      │                                                       │  │
│      └── runContinuation ────────────────────────────────────┘  │
│          • Virtual Thread의 실제 작업 내용 (Runnable)           │
│          • 중단/재개 가능한 실행 단위                           │
│  }                                                              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

3.2 전체 구조 시각화

┌─────────────────────────────────────────────────────────────────┐
│                      Virtual Thread 아키텍처                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │                    JVM (User Space)                       │  │
│  │                                                           │  │
│  │   ┌─────────────────────────────────────────────────────┐ │  │
│  │   │              ForkJoinPool (Scheduler)               │ │  │
│  │   │                                                     │ │  │
│  │   │  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐   │ │  │
│  │   │  │ Carrier-1   │ │ Carrier-2   │ │ Carrier-3   │   │ │  │
│  │   │  │ ┌─────────┐ │ │ ┌─────────┐ │ │ ┌─────────┐ │   │ │  │
│  │   │  │ │workQueue│ │ │ │workQueue│ │ │ │workQueue│ │   │ │  │
│  │   │  │ │[VT1,VT4]│ │ │ │[VT2,VT5]│ │ │ │[VT3]    │ │   │ │  │
│  │   │  │ └─────────┘ │ │ └─────────┘ │ │ └─────────┘ │   │ │  │
│  │   │  └──────┬──────┘ └──────┬──────┘ └──────┬──────┘   │ │  │
│  │   │         │               │               │          │ │  │
│  │   └─────────┼───────────────┼───────────────┼──────────┘ │  │
│  │             │               │               │            │  │
│  │   ┌─────────▼───────────────▼───────────────▼──────────┐ │  │
│  │   │                    힙 메모리                        │ │  │
│  │   │  ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐               │ │  │
│  │   │  │VT1 │ │VT2 │ │VT3 │ │VT4 │ │VT5 │ ... (경량)   │ │  │
│  │   │  └────┘ └────┘ └────┘ └────┘ └────┘               │ │  │
│  │   └────────────────────────────────────────────────────┘ │  │
│  │                                                           │  │
│  └───────────────────────────────────────────────────────────┘  │
│                              │                                  │
│                              │ 1:1 매핑                         │
│                              ▼                                  │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │                   OS (Kernel Space)                       │  │
│  │   ┌─────────────┐ ┌─────────────┐ ┌─────────────┐         │  │
│  │   │ OS Thread-1 │ │ OS Thread-2 │ │ OS Thread-3 │         │  │
│  │   └─────────────┘ └─────────────┘ └─────────────┘         │  │
│  └───────────────────────────────────────────────────────────┘  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

4. Virtual Thread 동작 원리

4.1 실행 흐름 단계별 설명

┌─────────────────────────────────────────────────────────────────┐
│  Virtual Thread 실행 흐름                                       │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  STEP 1: 작업 제출 (Submit)                                     │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  Virtual Thread 생성 시, runContinuation이                  ││
│  │  Carrier Thread의 workQueue에 push됨                        ││
│  │                                                             ││
│  │  VirtualThread.start()                                      ││
│  │         │                                                   ││
│  │         ▼                                                   ││
│  │  scheduler.submit(runContinuation)                          ││
│  │         │                                                   ││
│  │         ▼                                                   ││
│  │  carrierThread.workQueue.push(runContinuation)              ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                 │
│  STEP 2: 작업 실행 (Execute)                                    │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  ForkJoinPool이 Work Stealing 방식으로                      ││
│  │  Carrier Thread에 작업 분배                                 ││
│  │                                                             ││
│  │  Carrier Thread:                                            ││
│  │  1. workQueue에서 runContinuation pop                       ││
│  │  2. Virtual Thread를 mount (탑승)                           ││
│  │  3. runContinuation 실행                                    ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                 │
│  STEP 3: I/O 발생 시 park (일시정지)                            │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  I/O, Sleep, Lock 대기 시:                                  ││
│  │                                                             ││
│  │  1. Virtual Thread가 park 호출                              ││
│  │  2. runContinuation 상태를 힙 메모리에 저장                 ││
│  │  3. workQueue에서 pop (제거)                                ││
│  │  4. Carrier Thread에서 unmount (하차)                       ││
│  │  5. Carrier Thread는 다른 Virtual Thread 실행               ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                 │
│  STEP 4: I/O 완료 시 unpark (재개)                              │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  I/O 완료, Sleep 종료, Lock 획득 시:                        ││
│  │                                                             ││
│  │  1. Virtual Thread가 unpark 호출                            ││
│  │  2. scheduler가 runContinuation을 다시 스케줄링             ││
│  │  3. 어떤 Carrier Thread의 workQueue에 push                  ││
│  │  4. Carrier Thread에 mount되어 실행 재개                    ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

4.2 시각적 타임라인

┌─────────────────────────────────────────────────────────────────┐
│  Virtual Thread 생명주기 타임라인                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  시간 →                                                         │
│  ════════════════════════════════════════════════════════════   │
│                                                                 │
│  Virtual Thread:                                                │
│  ┌────────┐         ┌─────────────┐         ┌────────┐          │
│  │ 실행   │         │   WAITING   │         │ 실행   │          │
│  │(mount) │         │  (unmount)  │         │(mount) │          │
│  └───┬────┘         └──────┬──────┘         └───┬────┘          │
│      │                     │                    │               │
│      │ I/O 요청            │ I/O 완료           │               │
│      │ (park)              │ (unpark)           │               │
│      ▼                     ▼                    ▼               │
│                                                                 │
│  Carrier Thread:                                                │
│  ┌────────┐ ┌──────────────────────────┐ ┌────────┐             │
│  │ VT-1   │ │ VT-2 (다른 Virtual)      │ │ VT-1   │             │
│  │ 실행중 │ │ 실행중                    │ │ 재개   │             │
│  └────────┘ └──────────────────────────┘ └────────┘             │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │ 핵심: Carrier Thread는 놀지 않는다!                         ││
│  │       VT-1이 대기하는 동안 VT-2를 실행                      ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

5. park/unpark 동작 상세

5.1 Virtual Thread의 unpark 코드 분석

// VirtualThread.unpark() 내부 동작 (단순화)

void unpark() {
    // 1. 현재 상태 확인
    if (state == WAITING) {
        // 2. 상태 변경: WAITING → RUNNABLE
        state = RUNNABLE;

        // 3. 스케줄러에 작업 제출
        //    → Carrier Thread의 workQueue에 추가됨
        submitRunContinuation();
    }
}

void submitRunContinuation() {
    // ForkJoinPool에 작업 제출
    scheduler.execute(runContinuation);
    // → 내부적으로 어떤 Carrier Thread의 workQueue에 push됨
}

5.2 I/O 발생 시 park 동작

┌─────────────────────────────────────────────────────────────────┐
│  I/O 발생 시 Virtual Thread park 흐름                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  예: Socket 통신에서 응답 대기                                  │
│                                                                 │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │  1. VirtualThread에서 socket.read() 호출                 │   │
│  │                      │                                   │   │
│  │                      ▼                                   │   │
│  │  2. NIOSocketImpl.park() 호출                            │   │
│  │                      │                                   │   │
│  │                      ▼                                   │   │
│  │  3. 현재 스레드가 Virtual Thread인지 확인                │   │
│  │                      │                                   │   │
│  │          ┌───────────┴───────────┐                       │   │
│  │          │                       │                       │   │
│  │          ▼                       ▼                       │   │
│  │  [Platform Thread]       [Virtual Thread]                │   │
│  │  Net.poll() 호출         Poller.poll() 호출              │   │
│  │  (OS 커널에 요청)        (JVM 내부 처리)                 │   │
│  │  (비용 높음)             (비용 낮음)                     │   │
│  │                                   │                      │   │
│  │                                   ▼                      │   │
│  │                          Virtual Thread park             │   │
│  │                          • unmount from Carrier          │   │
│  │                          • 상태를 힙에 저장              │   │
│  │                          • Carrier는 다른 VT 실행        │   │
│  └──────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

5.3 Thread.sleep() 동작

┌─────────────────────────────────────────────────────────────────┐
│  Thread.sleep()에서의 Virtual Thread 분기                       │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  // JDK 21 Thread.sleep() 내부 (단순화)                         │
│                                                                 │
│  public static void sleep(long millis) {                        │
│      if (currentThread() instanceof VirtualThread vt) {         │
│          // Virtual Thread인 경우:                              │
│          // → JVM 내부에서 가볍게 park                          │
│          vt.sleepNanos(millis * 1_000_000L);                    │
│      } else {                                                   │
│          // Platform Thread인 경우:                             │
│          // → OS 커널에 요청 (기존 방식)                        │
│          sleep0(millis);  // native method                      │
│      }                                                          │
│  }                                                              │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  Virtual Thread sleep:                                      ││
│  │  • park → 힙에 저장 → Carrier 해제                          ││
│  │  • 타이머 완료 시 → unpark → 재스케줄링                     ││
│  │  • OS 개입 없음!                                            ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

6. 기존 Thread 모델과의 호환성

6.1 JDK 17 vs JDK 21 비교

┌─────────────────────────────────────────────────────────────────┐
│  LockSupport.park() 변화                                        │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  JDK 17 (기존):                                                 │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  public static void park() {                                ││
│  │      UNSAFE.park(false, 0L);  // 항상 native 호출           ││
│  │  }                                                          ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                 │
│  JDK 21 (Virtual Thread 지원):                                  │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  public static void park() {                                ││
│  │      Thread t = Thread.currentThread();                     ││
│  │      if (t.isVirtual()) {                                   ││
│  │          VirtualThreads.park();  // JVM 내부 처리           ││
│  │      } else {                                               ││
│  │          UNSAFE.park(false, 0L); // 기존 native 호출        ││
│  │      }                                                      ││
│  │  }                                                          ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                 │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  핵심: 기존 코드 수정 없이 Virtual Thread 지원!             ││
│  │        Thread.sleep(), Lock, I/O 모두 자동으로 동작         ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

6.2 NIOSocketImpl.park() 변화

┌─────────────────────────────────────────────────────────────────┐
│  Socket I/O에서의 변화                                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  JDK 17:                                                        │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  private void park(FileDescriptor fd, int event) {          ││
│  │      Net.poll(fd, event, -1);  // 항상 커널 poll            ││
│  │  }                                                          ││
│  │                                                             ││
│  │  → OS 스레드가 블로킹됨                                     ││
│  │  → 다른 작업 처리 불가                                      ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                 │
│  JDK 21:                                                        │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  private void park(FileDescriptor fd, int event) {          ││
│  │      Thread t = Thread.currentThread();                     ││
│  │      if (t.isVirtual()) {                                   ││
│  │          Poller.poll(fd, event, ...);  // JVM 내부 처리     ││
│  │          // → Virtual Thread만 park                         ││
│  │          // → Carrier Thread는 다른 VT 실행                 ││
│  │      } else {                                               ││
│  │          Net.poll(fd, event, -1);  // 기존 방식             ││
│  │      }                                                      ││
│  │  }                                                          ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

7. 핵심 요약

7.1 한 장으로 보는 Virtual Thread

┌─────────────────────────────────────────────────────────────────┐
│                   Virtual Thread 핵심 요약                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  1. 구조                                                        │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  VirtualThread                                              ││
│  │    ├── carrierThread: 실제 실행을 담당하는 Platform Thread  ││
│  │    ├── scheduler: ForkJoinPool (Carrier Thread Pool 관리)   ││
│  │    └── runContinuation: 실행할 작업 (중단/재개 가능)        ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                 │
│  2. 동작 원리                                                   │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  시작: runContinuation → workQueue에 push                   ││
│  │  실행: Carrier Thread가 workQueue에서 pop → 실행            ││
│  │  대기: I/O 발생 → park → unmount → 힙에 저장                ││
│  │  재개: I/O 완료 → unpark → workQueue에 push → 재실행        ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                 │
│  3. Platform Thread와의 차이                                    │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │           │ Platform Thread    │ Virtual Thread             ││
│  │  ─────────┼────────────────────┼────────────────────────    ││
│  │  매핑     │ OS 스레드 1:1      │ Carrier Thread N:1         ││
│  │  생성비용 │ ~1MB               │ 수백 바이트                ││
│  │  스케줄링 │ OS 커널            │ JVM (ForkJoinPool)         ││
│  │  I/O 대기 │ OS 스레드 블로킹   │ unmount (Carrier 해제)     ││
│  │  CS 비용  │ 높음 (시스템 콜)   │ 낮음 (JVM 내부)            ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                 │
│  4. 호환성                                                      │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  • 기존 Thread API 그대로 사용 가능                         ││
│  │  • sleep(), Lock, I/O 모두 자동으로 Virtual Thread 지원     ││
│  │  • JDK가 내부적으로 Virtual Thread 분기 처리                ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                 │
│  5. 핵심 이점                                                   │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  • I/O 대기 중 Carrier Thread가 다른 작업 처리              ││
│  │  • 수백만 개 Virtual Thread 생성 가능                       ││
│  │  • 컨텍스트 스위칭 비용 대폭 감소                           ││
│  │  • 기존 코드 수정 없이 성능 향상                            ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

7.2 PR 리뷰 시스템에서의 적용

┌─────────────────────────────────────────────────────────────────┐
│  GPT API 호출에 Virtual Thread 적용 효과                        │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  기존 (Platform Thread):                                        │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  Thread-1: [요청] ─────────────────────────── [응답처리]    ││
│  │                   │← 1~2초 대기 (스레드 점유) →│             ││
│  │                                                             ││
│  │  문제: 대기 중에도 ~1MB 메모리 점유                         ││
│  │        OS 스레드 낭비                                       ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                 │
│  Virtual Thread 적용 후:                                        │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  VT-1: [요청] ─park─▶ (힙에 저장, 수백 바이트)               ││
│  │                 │                                           ││
│  │  Carrier: ──────┼─▶ VT-2, VT-3, ... 실행 (놀지 않음)        ││
│  │                 │                                           ││
│  │  VT-1: ◀─unpark─ [응답처리]                                 ││
│  │                                                             ││
│  │  효과: 대기 중 Carrier 해제 → 다른 작업 처리                ││
│  │        수백만 개 동시 요청 가능                             ││
│  └─────────────────────────────────────────────────────────────┘│
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
profile
기록하는 공간

0개의 댓글