개요
Virtual Thread는 기존 Thread를 완벽하게 대체하는 개념이 아닙니다.
오히려 Thread, Process라기보다는 Task, Runnable에 가깝게 생각해야 이해하기 편합니다.
이번 글에서는 어떠한 부분에서 Virtual Thread와 Thread가 다른지, 어떠한 부분에서 같이 일하게 되는지 알아보겠습니다.
Thread LifeCycle에서의 Virtual Thread
Thread는 마구잡이로 사용하면 자원의 낭비를 불러올 수 있고, 때문에 ThreadPool에서 관리하여 사용합니다.
(ThreadPool - https://nangmandeveloper.tistory.com/7)
ThreadPool에서 동작하는 Thread는 다음과 같은 라이프사이클을 가질 것입니다.
1. ThreadPool에 Thread를 적정 수만큼 추가한다. (New)
2. ThreadPool이 Request를 기다린다. (Runnable)
3. 새 Request가 오면, 스레드가 해당 Request를 Pickup한다. (Running)
4. 해당 Request를 수행하기 위해 Task를 실행한다. (Running)
5. Task를 수행하는 동안 대기한다.(I/O작업, 외부 API통신 등) (wait, time_waiting, blocked)
6. Task종료 시, 스레드가 다시 실행되고 Response를 사용자에게 전달. (Running)
7. Request를 다시 기다린다... (Runnable)
위의 과정에서, Thread를 실제로 실행하는 부분은 3, 4, 6일 것입니다.
그렇다면 1, 2, 5, 7의 과정은 Thread가 직접 작업을 수행하는 부분이 아닌, 작업 수행을 위해 대기하는 순간일 것입니다.
Virtual Thread는 3, 4, 6번 과정에서 Platfrom Thread에 할당되고, 이외의 상황에서는 Heap에서 대기합니다.
Virtual Thread는 위의 그림과 같은 구조로 사용할 수 있습니다.
Platform Thread는 기존에 사용하던 일반 Thread로, 일반 Thread에 Virtual Thread. 즉, 작업들을 할당하고 할당 해제하는 순서로 동작합니다.
이때 Platform Thread Pool의 구현체인 ForkJoin Pool은 Platform Thread의 개수, 작업 순서 등을 직접 관리하는 스케줄러로 동작합니다.
아무런 설정을 하지 않았다면, 이 ForkJoin Pool의 Platform Thread는 CPU core개수로 설정됩니다. 최대 256까지 설정할 수 있습니다.(Default core 개수는 Logical core 개수로 결정됩니다.)
어차피 CPU core 1개는 하나의 Thread를 처리할 수 있으므로 CPU core개수만큼의 Platform Thread를 관리하고, Thread가 실제로 일을 하지 않는 경우에는 Task를 내리는 식으로 관리하는 것입니다.
이렇게 된다면, Thread를 사용하지 않는 1, 2, 5, 7에서 Task가 내려가기 때문에 해당 Thread는 다른 일을 할 수 있습니다. Thread라는 자원을 효율적으로 사용할 수 있는 것입니다.
Virtual Thread와 Thread의 차이
개요에서도 언급했듯이, Virtual Thread는 Thread의 확장판도 아니고, 대체하는 개념도 아닙니다.
대표적인 차이점은 아래와 같습니다.
1. 자원 저장소
Thread는 Code, Data, Heap영역을 같은 프로세스 내 스레드끼리 공유합니다.
하지만 Stack영역은 공유하지 않는 독립적인 자원으로 사용합니다.
Virtual Thread는 Heap에 저장됩니다. 작업이 진행되지 않으면 Heap에 저장되어 있다가, Park, Mount를 거치면서 Platform Thread의 Stack에 올라가 작업을 진행합니다.
이를 Platform Thread가 Virtual Thread의 Carrier Thread가 되었다고 표현합니다.
2. Blocked발생 상황
Thread만 사용하는 상황에서는, Thread가 Blocked상태가 되는 경우 그대로 기다립니다.
따라서, 모든 Thread가 Blocked상태가 되었다면 새로운 Thread를 만들어 Task를 처리합니다.
Virtual Thread를 사용하는 상황에서는, Thread가 Blocked되는 경우 Virtual Thread를 Heap으로 내립니다.
이후 대기중인 Virtual Thread를 Thread로 올려 작업을 계속 처리합니다.
Blocked된 Virtual Thread는 Blocked상태가 풀리면 다시 Thread로 올라가는 대기열에 참가합니다.
즉, Thread의 blocked상태를 최소화시키는 방법으로 동작합니다.
Blocked되었다 다시 올라온 Virtual Thread는 다른 Platform Thread에서 동작할 수도 있습니다.
3. 무한루프 발생 상황
while(true){
System.out.println("무한루프");
}
System.out.println("이 작업을 수행할 수 있을까?");
위의 두 작업이 있고, Thread를 하나만 사용한다면 일반 Thread, Virtual Thread 모두 아래 작업을 진행할 수 없습니다.
while(true){
System.out.println("무한루프");
Thread.sleep(1000);
}
System.out.println("이 작업을 수행할 수 있을까?");
Thread.sleep이 추가되면 다른 이야기가 될 것입니다.
Thread.sleep코드를 수행하는 경우, 해당 작업을 수행하는 Thread는 time_waiting상태가 됩니다.
일반 Thread만을 사용하는 경우, 아래의 코드는 마찬가지로 수행되지 않을 것입니다.
Virtual Thread를 사용하는 경우, Thread가 time_waiting되는 경우 다른 Virtual Thread가 올라갈 것이고, 해당 Virtual Thread가 아래의 코드를 수행할 것입니다.
4. 동기 코드 수행
일반 Thread의 경우 synchronized와 같은 동기 코드를 수행할 수 있습니다.
Virtual Thread의 경우, synchronized와 같은 동기 코드 수행시 Platform Thread와 Virtual Thread가 고정될 수 있습니다.
해당 문제점은 Pinned라 불리고, 성능을 저하시키거나 의도치 않은 결과를 불러올 수 있습니다.
이외에도 여러 차이점이 있겠지만, 위의 네 차이점이 가상스레드의 사용에 있어 가장 중요하다 생각했습니다.
다음 글에서는 Virtual Thread의 코드를 직접 보면서 Virtual Thread의 LifeCycle이 어떻게 이뤄지는지 알아보겠습니다.
'JAVA > Thread' 카테고리의 다른 글
[Thread] 5. Virtual Thread 알아보기 - 3 (0) | 2024.02.04 |
---|---|
[Thread] 3. Virtual Thread알아보기 (0) | 2023.12.19 |
[Thread] 2. JAVA에서 Thread사용하기 (1) | 2023.12.19 |
[Thread] 1. Process와 Thread의 차이 (0) | 2023.12.18 |