JDK 21 Virtual Thread 사용해보기

영석·2025년 1월 19일

Java

목록 보기
1/3
post-thumbnail

🧐 Virtual Thread 란?

  • 가상 스레드는 고처리량 동시 애플리케이션을 작성, 유지 관리, 디버깅하는 데 드는 노력을 줄여 주는 가벼운 스레드입니다. - 오라클 공식문서 -
  • 경량스레드라고도 불린다

🧐 기존 JDK 버전에서는..?

  • 기존 JDK에서는 플랫폼 스레드와, OS스레드가 1대1 Wrapping 된 구조이다.
    이러한 구조는 OS thread에 종속적이며 이는 곧 CPU 스팩에 종속적이다는 말과 같다

‼️ 한계점

  • 메모리 소비 - OS thread는 1~2MB 메모리 할당받음
  • 운영체제 부하 - 스레드가 많아질 경우 운영체제에서 스케줄링에 부담
  • blocking I/O - 입출력/요청시 스레드는 리소스를 점유하는 동시에 대기 상태

이러한 문제점을 해결하기 위해 가상스레드(Virtual Thread)가 등장한다

📚 JDK 21 버전 부터는..?

  • 가상스레드를 도입!

가상스레드는 JVM레벨에서 관리되는 스레드이며 수백만 개의 가상스레드 생성해도
메모리와 CPU리소스를 적게 소모한다.

먼저 생각해보면 가상스레드를 사용한다면 기존 OS스레드에 의존적이지 않게 되며
JVM 자체 스케줄러를 사용하기 때문에 스위칭 비용이 작아짐.
또한..

Blocking I/O 완화

가상 스레드는 1대 1 Wrapping 되어 있지 않기 때문에 언제든지 마운트/언마운트로 플랫폼 스레드에 작업을 할당할 수 있음

가상스레드 1번이 대기상태에 돌입하면, 작업 끝날때까지 플랫폼 스레드를 점유하지 않고 가상스레드 2번이 작업을 시작하고, 가상스레드 1번이 대기상태에서 풀려나면 다시 플랫폼 스레드를 마운트하여 사용한다

🥑 그래서 언제 사용하지?

  • 시나리오 1
    매일 아침 6시에 만 명 이상이 존재하는 서비스에 모든 유저의 포인트를 +30 할 예정이다.
    @Scheduled(cron = "0 0 6 * * *") // 매일 아침 6시 실행 스캐줄
    public void updateAllUserPoints() {
        List<User> users = getListAllUsers(); // 모든 유저 리스트 가져오기

        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
            for (User user : users) {
                executor.submit(() -> { // 작업 제출
                    updateUserPoints(user); // 유저 포인트 업데이트
                });
            }
        }
    }
  • Executors
    자바의 스레드 풀 관리 유틸리티 클래스임.
  • newVirtualThreadPerTaskExecutor
    JDK 21에 추가된 가상스레드기반의 스레드 풀 생성하는 메서드

🌱 기대효과(근사치)

만약 10000명의 유저의 업데이트 작업을 한다면?

  • 1명당 작업 시간 200ms 소요 (가정), DB 연결 풀 (100)

🌱 기존 방식

(10000/100) * 200ms => 20000ms => 20초

🌱 가상스레드 방식

  • Blocking I/O의 성능 최적화 (적은 데이터(10~20%) 대용량데이터일때 (50% 이상) 가정) 20% 성능 개선 가정

(10000 (200ms * 80%))/100 => 16초

👍 결론

  • DB 등 다른 부분의 성능을 향상해 주는 기술은 아님 Blocking I/O의 시간을 효율적으로 처리하여 애플리케이션 차원에서 대기시간을 감소하여 작업시간을 단축하는 효과가 크다.
profile
느리게 갱신되는 개발실력 - >_0

2개의 댓글

comment-user-thumbnail
2025년 1월 20일

깔끔하네요👍

1개의 답글