Virtual Thread 도입 이전
기존 자바의 Thread는 OS와 1:1로 매핑되는 플랫폼 스레드를 사용한다.
따라서 자바에서 Thread를 생성하고 사용하는 데는 아래와 같은 단점들이 존재한다.
- Thread의 개수가 제한적이다.
- Thread의 생성과 유지 비용이 매우 비싸다.
- Thread가 IO작업을 하는 동안 사용되는 동안 다른 작업을 할 수 없다, (Blocking이 일어난다.)
위와 같은 단점을 조금이나마 해결해 보고자 스레드를 효율적으로 사용할 수 있는 Thread Pool을 만들어서 사용하곤 했다.
예를 들어, Spring Boot 같은 경우에 하나의 요청을 처리하기 위해 하나의 Thread를 만들어 관리하는데, 많은 요청이 들어오면 뒤에 응답은 앞에 요청이 모두 끝난 후에야 처리될 수 있다.
(물론 Reactive Programming으로 위 단점들을 상쇄시킬 수 있지만...)
이후, 2017년에 자바의 친척인 Kotlin에서 경량 스레드 모델인 coroutine이 도입되었다.
(Go에선 goroutine라는 경량 스레드 모델이 이미 존재했다.)
Virtual Thread란?
JDK21에 정식 기능으로 도입된 경랑 스레드 모델이다.
Virtual Thread는 기존 커널 스레드를 직접적으로 호출하는 방식과 달리 Virtual Thread와 커널 사이에서 JVM의 스케쥴러를 사용하여 Carrier Thread라는 매개채를 통해 작업이 이루어진다.
따라서 Virtual Thread는 생성 및 모든 유지 관리를 커널이 아닌 JVM에서 하게 된다.
Thread VS Virtual Thread
기존 Thrad는 2MB의 스택 메모리를 가지는데, 따라서 Context Switching 과정에서 많은 메모리를 사용하여야 한다.
또한, Thread 생성을 위해 커널과 통신하기 때문에 생성 비용도 크다.
하지만 Virtual Thread는 JVM에 의해 생성되고 관리되기 때문에, 기존 Thread에 비해 자원 사용량이 매우 작다.
Thread | Virtual Thread | |
Stack 사이즈 | ~2mb | ~10kb |
생성시간 | ~1ms | ~1µs |
컨텍스트 스위칭 비용 | ~100µs | ~1µs |
실제 결과를 보면, Virtual Thread는 Thread보다 100배 이상의 효율성을 지니고 있다.
Virtual Thread는 어떻게 동작하는가?
동작 원리를 알아보기 전에 Virtual Thread에 사용되는 용어 정리를 하고 가자.
- Carrier Thread: 실질적으로 작업이 진행되는 Thread이다.
- Scheduler: 위 Carrier Thread를 처리하기 위한 스케쥴링을 위한 ForkJoinPool이다.
- runContinuation: Virtual Thread가 실제로 실행할 작업(Runnable)이다.
그럼 이제 실질적인 동작 과정을 보자.
- Virtual Thread의 runContinuation을 Carrier Thread의 Work Queue에 Push한다..
- Work Queue의 runContinuation들은 Virtual Thread의 Scheduler에 의해 Work-Stealing방식으로 처리된다.
- 처리중인 runContinuation가 IO, interrupt 혹은 완료 시 Work Queue에 Pop되고 Park되어 Heap 메모리로 돌아간다.
참고문헌
https://techblog.woowahan.com/15398/
'Java' 카테고리의 다른 글
[Java] Dynamic Proxy (0) | 2024.05.23 |
---|---|
[Java] Proxy (0) | 2024.05.16 |