JDK 21부터 기존 플랫폼 스레드의 단점을 보완하고, 동시 처리량을 높이기 위해 가상 스레드가 도입됐다.
Thread 종류
커널 수준 스레드 (Kernel-Level Threads)
스레드의 생성, 스케줄링 및 관리를 직접 OS 커널이 담당합니다.
커널 수준 스레드는 자원 관리 및 멀티 프로세싱 환경에서의 스케줄링 측면에서 장점이 있으나, 스레드 생성 및 컨텍스트 스위칭에 높은 오버헤드가 있을 수 있습니다.
사용자 수준 스레드 (User-Level Threads)
사용자 영역의 라이브러리나 애플리케이션에 의해 관리되는 스레드입니다.
운영체제(OS) 커널이 아닌 사용자 공간(User Space)에서 관리되는 스레드로, OS의 개입 없이 라이브러리나 런타임이 직접 스레드를 생성하고 관리합니다.
운영 체제의 커널로부터 독립적으로 스케줄링되며, 스레드 관리에 필요한 모든 작업을 사용자 영역에서 처리하는 스레드입니다.
JVM Thread Pool
자바에서 Thread Pool은 ExecutorService 인터페이스를 통해 제공된다.
기존 JVM 스레드 풀
JVM Heap 메모리에 여러 사용자 수준 스레드를 커널 수준 스레드와 1대1 매칭으로 생성하여 풀에 담아두고 있다.
자바에서 스레드의 스케줄링은 운영 체제의 스레드 스케줄러를 위임받은 JVM가 관리하게 됩니다.
자바는 왜 스레드를 미리 만들어 두는가?
1. OverHead 문제
운영체제에서 스레드 생성과 종료 과정은 커널을 통해 이루어진다.
➡️ 새로운 스레드를 만들 때 운영체제의 개입이 필요하여 커널 모드로 전환되는 컨텍스트 스위칭 비용이 발생한다.
그래서 자바 애플리케이션은 실행 시 미리 여러 개의 스레드를 생성해두고 필요할 때 재사용한다.
컨텍스트 스위칭(Context Switching)이란?
운영체제는 CPU가 여러 스레드를 동시에 실행하는 것처럼 보이게 하기 위해 한 스레드에서 다른 스레드로 실행을 변경할 때 컨텍스트 정보를 저장하고, 새로운 컨텍스트를 불러오는 과정을 수행합니다.
이를, 컨텍스트 스위칭이라 한다.
스레드 생성 시 왜 컨텍스트 스위칭 비용이 발생하는 이유
2. 비용 문제
새로운 스레드를 생성하면 커널이 개입해야 합니다. (커널 모드 전환)
- 새로운 스레드를 생성하는 과정에서 운영체제의 커널이 개입해야 한다.
- 일반적인 사용자 모드(User Mode) 작업과 다르게, 스레드 생성 및 관리 작업은 커널 모드(Kernel Mode)에서만 실행된다.
- 이 과정에서 사용자 모드 → 커널 모드 전환 비용이 발생한다.
📌 생성 과정 보기
- 사용자 모드에서 실행 중이던 애플리케이션이 시스템 호출(System Call) 을 통해 커널 모드로 전환 (컨텍스트 스위칭 1)
- 운영체제는 새로운 스레드를 위한 메모리 할당, 스택 생성, 스레드 테이블 등록 등의 작업이 수행된다.
- 작업이 끝난 후 다시 커널 모드 → 사용자 모드로 전환 (컨텍스트 스위칭 2)
기존 스레드는 왜 무거운가?
3. 메모리 문제
운영체제의 커널 스레드는 보통 1 ~ 2 MB의 스택 메모리를 차지한다.
- 각 스레드는 독립적인 스레드를 가지며, 지역 변수와 호출 스택을 관리합니다.
- Thread Context 정보를 유지하여, CPU 레지스터, 프로그램 카운터, 메모리 페이지 정보 등을 관리합니다.
I/O 작업 중 블로킹되는 스레드
컴퓨터에서 데이터 연산과 I/O 작업은 서로 다른 하드웨어가 담당합니다.
- CPU - 데이터 연산
- I/O 장치 - 파일 입출력, 네트워크 요청 담당
운영체제가 I/O 요청을 관리하고, 느린 입출력 작업을 진행하는 동안 다른 작업을 수행하기 위해 스레드를 통해 처리합니다. 이때 스레드는 I/O 작업에 블로킹됩니다.
Java 21 가상 스레드
기존 플랫폼 스레드
- 플랫폼 스레드는 OS가 관리하는 전통적인 자바 스레드 모델에서 사용되는 스레드로, Java 가상 머신(JVM)이 운영 체제의 기능을 활용하여 생성합니다.
- 높은 연산량을 요구하는 계산 작업등에 작업에 주로 사용되며 상대적으로 많은 리소스를 소비하며, 스레드의 수는 시스템의 리소스에 의해 제한됩니다.
기존 Java의 스레드 모델은 Native Thread로, Java의 유저 스레드를 만들면 Java Native Interface(JNI)를 통해 커널 영역을 호출하여 OS가 커널 스레드를 생성하고 매핑하여 작업을 수행합니다.
Java의 스레드는 I/O, interrupt, sleep과 같은 상황에 block/waiting 상태가 되는데, 이때 다른 스레드가 커널 스레드를 점유하여 작업을 수행합니다. (컨텍스트 스위치)
가상 스레드
JVM 위에서 실행되는 경량 스레드입니다.
기존 운영체제 기반 플랫폼 스레드보다 훨씬 가벼우며, 적은 리소스를 사용합니다.
커널 스레드와 1:1 매칭이 아니고, 수천 개 이상의 가상 스레드를 하나의 커널 스레드에서 사용할 수 있습니다.
- 가상 스레드는 스택 크기가 필요한 만큼 메모리를 사용하여 동적으로 조정됩니다.
- OS 스레드는 크기가 제한적이지만, 가상 스레드는 수천~수백만 개까지 생성 가능하여 이벤트 기반 비동기 처리에 적합합니다.
가상 스레드는 필요할 때만 커널 스레드를 사용합니다.
가상 스레드는 기본적으로 커널 스레드(Carrier Thread)에 의해 실행되지만, I/O 대기 상태에 들어가면 즉시 커널 스레드에서 분리됩니다.
- 일반 커널 스레드의 문제점:
스레드가 블로킹 I/O(예: HTTP 요청, DB 쿼리) 상태일 때,→ OS 커널 스레드가 계속 점유된다.
따라서, CPU를 사용하지 않으면서도 불필요한 리소스 소비 발생합니다.
- 가상 스레드의 동작 방식:
가상 스레드는 I/O 대기 상태가 되면 커널 스레드에서 즉시 해제됩니다.
다른 가상 스레드가 같은 커널 스레드를 사용 가능하여, 컨텍스트 스위칭 비용없이 다른 가상 스레드로 I/O 작업을 처리할 수 있습니다.
✅ 즉, 가상 스레드는 CPU를 낭비하지 않고 대기 상태에서 빠르게 전환 가능합니다.
케리어 스레드 (Carrier Thread)
가상 스레드는 케리어 스레드 위에서 실행됩니다.
케리어 스레드란 가상 스레드를 실행하는 실제 운영체제 기반 스레드이며, 플랫폼 스레드라고도 할 수 있습니다.
- 여러 개의 가상 스레드를 관리하고 실행합니다.
- CPU와 직접 상호작용하며 실제로 연산을 수행합니다.
- OS 스레드의 개수 제한을 극복하고자, 캐리어 스레드와 가상 스레드를 통해 더 많은 동시 실행을 가능하게 합니다.
참고자료
'JAVA' 카테고리의 다른 글
Java 프로그램 실행 과정, JVM 구조 이해하기 (0) | 2025.02.15 |
---|---|
Java string, stringbuilder를 이용해 문자열 합치기 (0) | 2024.11.23 |
Java Record 컴팩트 생성자(compactconstructor) 이해하기 - record, @QueryProjection 적용 (0) | 2024.11.18 |
Java if문 최적화와 가독성 향상 방법 - 클린코드 작성 방법 (0) | 2024.11.12 |
[Java] Java Shutdown Hook 이란 - System.exit() (2) | 2024.09.25 |