본문 바로가기

프로그래밍/자바

Virtual Thread와 소켓 IO 구현 정리

이 글은 스프링캠프 2025에서 박성훈 정승주님의 발표를 바탕으로 정리하여 작성한 글 입니다. 해당 과정에서 제 실수로 잘못된 정보를 기재 하였을 수도 있는 점 양해 부탁 드립니다.

JDK 13 이전 vs 이후 소켓 구현 변화

JDK 13 이전 (레거시 구현)

  • PlainSocketImpl: JDK 1.0부터의 레거시 Java와 C 코드 혼합
  • 네이티브 함수 위주의 소켓 구현
  • OS Socket 자원을 FileDescriptor에 위임
  • synchronized 메서드 사용으로 Virtual Thread 비호환

JDK 13 이후 (새로운 구현)

  • NioSocketImpl: PlainSocketImpl의 드롭인 대체
  • NIO 구현과 같은 JDK 내부 인프라 공유
  • java.util.concurrent 락 사용으로 Virtual Thread 호환

NioSocketImpl의 핵심 동작

소켓 모드 전환

소켓은 처음에 블로킹 모드로 설정되다가, 타임아웃이나 Virtual Thread에서 블로킹 작업이 시도되면 논블로킹 모드로 변경

Virtual Thread에서의 I/O 처리 흐름

  1. Virtual Thread에서 소켓 읽기 시도
  2. 논블로킹으로 먼저 시도 (tryRead())
  3. 데이터가 없으면 (IOStatus.UNAVAILABLE) Poller에게 감시 요청
  4. VirtualThreads.park() 호출로 Virtual Thread 일시정지
  5. I/O가 준비되면 Virtual Thread가 unpark되어 재개

핵심 클래스들 상세

Poller 클래스

  • 중단된 Virtual Thread를 재개하는 역할
  • NioSocketImpl이 Poller에게 감시 요청
  • 내부적으로 Selector API 사용

Selector API

  • 등록된 소켓의 상태를 감지할 수 있는 API
  • Selector API의 추상 클래스 구조
  • OS별 구현체가 다름:
    • Linux: EPollSelectorImpl (epoll)
    • macOS: KQueueSelectorImpl (kqueue)
    • Windows: WindowsSelectorImpl (select)

NativeDispatcher

  • 실제 네이티브 소켓 연산 담당
  • SocketDispatcher: 소켓 관련 네이티브 호출
  • 연산 재시도 필요시 Poller가 감지

전체 흐름도

Virtual Thread 소켓 읽기 요청
         ↓
   NioSocketImpl.read()
         ↓
     tryRead() 시도
         ↓
   데이터 있음? ──── YES ──→ 데이터 반환
         ↓ NO
   IOStatus.UNAVAILABLE
         ↓
   Poller.pollRead() 호출
         ↓
   Selector에 감시 등록
         ↓
   VirtualThreads.park()
         ↓
   [Virtual Thread 일시정지]
         ↓
   소켓에 데이터 도착
         ↓
   Selector 이벤트 감지
         ↓
   Virtual Thread unpark()
         ↓
   다시 tryRead() 시도
         ↓
      데이터 반환

핵심 달성 목표

실제로는 논블로킹 수준의 IO를 수행하는 블록킹 IO

  • 소켓 자체: 논블로킹 모드로 설정
  • 애플리케이션 레벨: 기존과 동일한 블로킹 API 제공
  • Virtual Thread: park/unpark로 블로킹 시뮬레이션
  • 결과: OS 스레드 자원 효율성과 사용 편의성 모두 확보

시기와 배경

이 모든 재구현은 Virtual Thread 정식 출시 전에 미리 완료되었습니다:

JEP 버전 시기 내용
JEP 353 Java 13 2019 Socket/ServerSocket 재구현
JEP 373 Java 15 2020 DatagramSocket/MulticastSocket 재구현
Virtual Thread JDK 19 2022 프리뷰 버전
Virtual Thread JDK 21 2023 정식 릴리즈

레거시 지원

기존 구현으로 되돌리기 가능:

-Djdk.net.usePlainSocketImpl=true

결론

이런 구조로 Virtual Thread는 기존 블로킹 IO 코드를 전혀 수정하지 않고도 높은 동시성을 달성할 수 있게 되었습니다