VirtualThread 아는 체하기
(1) Virtual Thread이란?
JDK 21에 추가된 경량 쓰레드이다. OS 쓰레드를 사용하지 않고 JVM 내부 스케줄링을 통해 쓰레드를 생성한다.
수십만~수백만개의 쓰레드를 동시에 사용할 수 있다.
(2) 일반 Thread이란?
기존의 일반 Thread는 OS Thread를 그대로 매핑한 것이다. 이를 Platform Thread
라고 한다.
Java Thread는 실제로 OS Thread를 사용한 것이다. Thread를 생성할 때 커널 영역에 접근하기 위해, 시스템 콜을 해야한다. 이로 인하여 오버헤드가 발생한다.
이러한 배경으로 생성 갯수가 제한적이고, 생성하고 유지하는 비용이 크다.
그래서 Thread를 무한히 생성할 수 없기 때문에 Thread Pool으로 관리해왔다.
미리 Thread를 만들어두고, 재사용하고, 생성 개수를 제한해왔다.
(3) Virtual Thread의 등장 배경
(3-A) 기존 Thread 구조의 한계
기본적인 web request 처리 방식은 Thread per request이다. 하나의 요청 당, 하나의 쓰레드이다.
처리량을 높이려면 쓰레드를 늘려야하나, OS Thread의 제약으로 무한히 늘릴 수 없다.
(3-B) Blocking I/O 으로 인한 대기 시간 증가
Thread에서 I/O 작업을 처리할 때 Blocking이 일어난다. 즉, 작업을 처리하는 시간보다 대기하는 시간이 길다.
(3-C) Reactive Programming의 한계
Thread 대기 시간을 줄이기 위해 Webflux 같은 개념이 등장했다. 그러나 코드를 작성하고 이해하기 어렵다.
Reactive Programming 을 하려면 reactive를 지원하는 라이브러리를 사용해야한다. 그런데 레퍼런스가 부족해서 사용하기 쉽지 않다.
(4) Virtual Thread의 장점
- 처리량이 높다.
- Blocking I/O 줄일 수 있다.
- 쓰레드 사용 메모리가 적다.
- 러닝 커브가 낮다
- 기존 스레드 구조를 그대로 활용
- MVC 환경의 동기식 스타일로 쉽게 코딩을 할 수 있다.
(5) Virtual Thread 구조
(6) Virtual Thread 사용팁
- threadpool이 없다. 태스크가 필요하면 virtual thread를 쭉쭉 만들자.
- GC가 알아서 메모리를 정리해주길 기다리자.
- synchronized block이나 JNI을 사용하는 경우 worker thread (platform thread/carieer thread)가 고정되어 pinning 현상이 발생할 수 있다. ReentrantLock을 사용하여 우회하자.
(7) 기존 Thread이 여전히 필요한 상황
기존 Thread와 Virtual Thread는 각각 장단점이 있다.
Virtual Thread은 Blocking I/O가 발생하는 작업의 대기 시간을 줄여주는 이점이 있다. 그러나 CPU 작업량이 높은 경우에는 큰 이점이 없다.
Virtual Thread를 무한히 늘리더라도 물리 시스템이 이를 받아주지 못하는 경우가 있다. 예를 들어, DB Connection이 그러하다. 이 경우, 기존 Thread와 Thread Pool을 사용하여 적정 사용량을 조정해야한다.