스레드에 대해
by 달쿨Process
프로세스는 운영체제에 의해 메모리 공간을 할당받아 CPU에서 실행되는 프로그램을 의미한다. 즉 정적 상태(메모리에 올라가 있지 않은 상태, 단순 코드 파일)의 프로그램을 실행하면 운영체제로부터 CPU를 할당 받아 동적 상태로 변하는데 이 상태의 프로그램이 프로세스인 것이다. 그리고 이 안에서 실제로 작업을 처리하는 실행 단위가 스레드이다.
Thread
스레드는 프로세스가 할당 받은 자원을 이용하는 실행 단위로, 프로세스의 한계를 해결하기 위해 만들어진 개념이다. 이는 기술이 발전함에 따라 프로세스 하나만으로는 프로그램을 실행하기 힘들어 졌기 때문인데, 그렇다고 한 프로그램을 여러개의 프로세스로 만들면 메모리를 차지하고 CPU에서 할당받는 자원이 중복되기 때문에(프로세스가 자신에게 할당된 메모리 내의 정보에만 접근할 수 있기 때문이다.) 이 방법으로는 해결할 수 없었고, 그래서 나오게 된 것이 스레드이다.
스레드는 프로세스와 다르게 스레드 간 프로세스의 메모리를 공유하며 작동한다. 프로세스는 운영체제로부터 메모리 영역을 Code/Data/Stack/Heap의 형식으로 할당받는데, 이 때 스레드는 해당 메모리 영역 내에서 Stack 형식의 메모리 영역은 따로 할당받고 나머지 Code/Data/Heap 메모리 영역은 공유한다. 즉 각각의 스레드는 별도의 Stack 공간을 할당받고 그 외 영역은 같은 프로세스 내의 다른 스레드들과 공유한다.
때문에 멀티 스레드를 사용하면 자원 생성과 관리의 중복성을 최소화 해 실행 능력을 향상시킬 수 있다. 프로세스의 컨텍스트 스위칭에 비해 오버헤드도 절감할 수 있다.
Context Switching
멀티 스레드를 사용하면 여러 작업이 동시에 실행되는 것처럼 보이지만, 실제로는 여러 스레드가 번갈아가며 실행된다. 하나의 CPU 코어는 한 시점에 한 가지 작업만 처리할 수 있기 때문이다. 이렇게 싱클 코어에서 여러 작업이 동시에 실행되는 것처럼 보이게 하는 성질을 동시성(Concurrency)이라고 한다. (*다른 개념인 병렬성은 여러 CPU 코어를 사용해 실제로 동시에 처리하는 것이다.) 이 때 내부에서 작업들을 바꾸는 것이 컨텍스트 스위칭이다.
즉 컨텍스트 스위칭은 CPU가 프로세스(또는 스레드) 간의 실행을 전환하는 기술이다. 간단히 정리하면, 동작 중인 프로세스가 스케줄러에 의해 대기를 하게 되면 해당 프로세스의 상태를 PCB(*스레드의 경우 TCB)에 저장하고, 새 프로세스의 상태를 PCB로부터 다시 가져와 교체하는 작업이다.
Scheduling
스케줄러는 메모리를 효율적으로 사용할 수 있도록 어떤 작업에 자원을 할당할 것인지 방법과 순서를 결정하는 모듈이며, 스케줄링이란 결국 어떤 작업에 CPU를 할당할 지 결정하는 작업이다.
*이 때 커널 수준 스레드의 스케줄링만 운영 체제에 의해 직접 관리되며, 사용자 수준 스레드의 스케줄링은 애플리케이션에 의해 관리된다. 커널은 사용자 수준 스레드의 존재를 알지 못하기 때문이다.
컨텍스트 스위칭 오버헤드
스케줄링을 위해 프로세스의 상태를 저장하는 PCB(Process Control Black) 자료구조는 프로세스가 실행됨과 동시에 메모리에 함께 생성되고, 종료 시 삭제된다. 스레드에 대한 정보는 TCB(Thread Control Block) 자료구조에 저장되며, 이것도 스레드가 생성될 때 운영체제에 의해 생성되고 스레드가 종료되어 소멸될 때 함께 소멸된다. (*프로세스 안에 스레드가 포함되는 것 처럼 TCB도 PCB 안에 LinkedList로 구현되어 있다.)
컨텍스트 스위칭 과정에선 이 자료구조를 저장하고 복원하는 데에 오버헤드(시간, 메모리)가 발생하게 된다. (저장하고 복원하는 동안에 CPU가 아무런 일도 못하게 되기 때문이다. 이 상황이 잦아질 수록 성능이 저하되는 것이다.)
이 때, 스레드들은 힙 등의 영역은 프로세스의 자원으로 공유해서 사용하기 때문에 자신의 TCB에는 stack 정보나 간단한 포인터 정보만을 저장한다. 때문에 PCB보다 TCB가 가볍다. 이로 인해 더 빨리 읽고 쓸 수 있으므로 프로세스의 컨텍스트 스위칭보다 스레드의 컨텍스트 스위칭이 더 빠르다. (*프로세스 컨텍스트 스위칭이 일어날 경우 CPU 캐시 메모리의 초기화가 이루어지기 때문에 더 부담이 간다. 스레드의 경우 일부 정보만 변경되므로 보통 CPU 캐시 메모리 초기화가 이루어지지 않는다.)
여기서 생각할 수 있는 점은 스레드가 많더라도 항상 성능이 좋아지진 않는다는 점이다. 스레드가 많아질수록 결국 컨텍스트 스위칭 횟수가 많아지며, 오버헤드도 같이 많아지게 되는 것이다. 또한 과도하게 많은 스레드를 사용할 경우 놀게되는 잔여 스레드들이 불필요하게 자원을 차지하게 된다. (그리고 이런 스레드들과도 컨텍스트 스위칭은 일어나기 때문에 오버헤드도 똑같이 생긴다.)
때문에 무조건 스레드를 많이 생성하면서 멀티 스레드 환경을 구성하는 것이 좋은게 아니며, 노는 스레드의 개수를 최소화하고 스레드 풀 같은 매커니즘을 이용해 스레드 개수를 관리하며 리소스 낭비를 최소화해야 한다.
Thread Pool
스레드 풀이란 미리 일정 개수의 스레드를 생성해 관리하고, 작업 큐에 들어오는 작업들을 하나씩 꺼내 처리하는 기법으로, 작업이 발생하면 만들어둔 스레드 중 사용 가능한 스레드를 할당하여 작업을 처리한다. 그리고 처리가 완료되면 스레드가 제거되는 것이 아닌, 다시 스레드 풀로 돌아가 재사용된다. 이에 따라 스레드의 생성과 삭제에 따른 오버헤드를 줄일 수 있다. 또 스레드의 개수를 제한할 수 있어 처리 요청이 많아지더라도 스레드의 전체 개수가 일정하도록 해준다.
JVM의 스레드
초기에는 N:1 모델, Green Thread를 사용하다가 1:1 모델, Native Thread를 사용하게 되었다.
스레드 유형
스레드에는 사용자 수준 스레드(User Level Threads)와 커널 수준 스레드(Kernel Level Threads)의 두 가지 유형이 있다. 사용자 수준 스레드는 커널의 개입 없이 스레드 라이브러리를 통해 사용자 영역 생성되고 관리되는 스레드이며, 커널 수준 스레드는 커널 영역에 생성되며 운영체제에 의해 직접 관리되는 스레드이다.
스레드를 사용하려면 CPU 스케줄러가 스레드를 CPU에 스케줄링 해 줘야 하는데, CPU 스케줄러는 커널의 일부이므로 사용자 수준 스레드에 대해 인식하지 못하기 때문에 사용자 수준 스레드는 커널 수준 스레드에 매핑되어 실행하게 된다. 이 때 매핑 형태가 다대일(사용자 수준), 일대일(커널 수준), 다대다(혼합형)의 세 가지 방식으로 구현된다. (Multi Threading Model 이라고 한다.)
Multi Threading Model
Many-to-One Model
다대일(N:1) 모델은 여러 사용자 스레드(N)를 하나의 커널 스레드(1)에 매핑한 형태이다. 스레드가 사용자 공간의 스레드 라이브러리에 의해 관리되며, 커널 입장에서는 해당 스레드의 존재에 대해 인식하지 못하고 하나의 프로세스로 인식된다.
이로 인해 프로세스에 속한 스레드 중 하나라도 블로킹되면 모든 사용자 스레드가 같이 멈추게 되는 단점이 있다. 실제로 동작하는 것은 하나의 커널 스레드이기 때문이다.(멀티코어를 활용하지 못한다.) 하지만 커널의 개입 없이 라이브러리를 통해 유저 스레드끼리 스위칭이 일어나기 때문에 컨텍스트 스위칭 속도가 빠르다.
One-to-One Model
일대일(1:1) 모델은 각 사용자 스레드(1)를 각각 하나의 커널 스레드(1)에 매핑한 형태이다. 때문에 다대일 모델과 다르게 하나의 스레드가 블로킹 되더라도 다른 스레드가 실행될 수 있어 때문에 병렬 실행이 용이하다. 하지만 사용자 스레드를 만들기 위해서는 커널 스레드를 만들어야 한다는 단점이 있다.
Many-to-Many Model
다대다(N:M) 모델은 이전 두 모델을 혼합해 단점을 절충한 방법이다. 여러개의 사용자 스레드를 그보다 작거나 같은 커널 스레드로 멀티플렉스하는 방법이다. 실제로는 구현하기 어렵다.
정리하면 스레드는 JVM과 운영체제의 상호작용을 통해 동작하며 JVM이 자바 스레드를 생성하고 관리하고 해당 스레드가 1:1로 커널 스레드에 매핑되어 실제 CPU 스케줄링과 실행은 운영체제가 한다. (JNI를 통해 OS 스레드 생성 기능을 호출한다.)
참고 링크
- 커널/사용자 레벨 스레드 https://xbtmdals7.tistory.com/42
- 커널/사용자 레벨 스레드 https://jun-n.tistory.com/156
- 커널/사용자 레벨 스레드 https://velog.io/@pp8817/OS-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%88%98%EC%A4%80-%EC%8A%A4%EB%A0%88%EB%93%9C%EC%99%80-%EC%BB%A4%EB%84%90-%EC%88%98%EC%A4%80-%EC%8A%A4%EB%A0%88%EB%93%9C%EC%9D%98-%EC%B0%A8%EC%9D%B4%EB%8A%94
- 스레드 https://coding-start.tistory.com/199
- 스레드 https://beststar-1.tistory.com/6
- 스레드 https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%E2%9A%94%EF%B8%8F-%EC%93%B0%EB%A0%88%EB%93%9C-%EC%B0%A8%EC%9D%B4
- 프로세스 + 스레드 https://velog.io/@raejoonee/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%99%80-%EC%8A%A4%EB%A0%88%EB%93%9C%EC%9D%98-%EC%B0%A8%EC%9D%B4
- 콘텍스트 스위칭 https://beststar-1.tistory.com/26
- 스레드 스케줄링 https://wikidocs.net/232057
- 스레드 스케줄링 https://gengmi.tistory.com/entry/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%99%80-%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%A7%81
- 스레드 많이 쓸수록 성능이 좋아질까 https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-Is-more-threads-always-better
- 멀티 프로세스/스레드 https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-multi-process-multi-thread
- 스레드 풀 https://coji.tistory.com/171
- 스레드 풀 https://jtm0609.tistory.com/159
- JVM 스레드 https://velog.io/@tlarbals824/JVM%EC%97%90%EC%84%9C%EC%9D%98-%EC%8A%A4%EB%A0%88%EB%93%9C
- JVM 스레드 + 멀티 스레딩 모델 https://e-una.tistory.com/70
- JVM 스레드 동작 원리 https://f-lab.kr/insight/java-thread-management-20251127
- 사용자/커널 수준 스레드 https://blogimadetosee.tistory.com/85
'공부' 카테고리의 다른 글
| GC에 대해 (0) | 2026.03.29 |
|---|---|
| JVM에 대해 (0) | 2026.03.29 |
| AOP에 대해 (0) | 2026.03.18 |
| 역색인(Inverted Index) 구조 (0) | 2025.04.24 |
| RabbitMQ 개념 (3) | 2025.01.14 |
블로그의 정보
note
달쿨