본문 바로가기

CS

Thread Pool이란?

쓰레드 풀은 자바 개발을 하면서 비동기 처리, 멀티쓰레드 작업, 또는 병렬 처리와 같은 작업을 수행할 때 자주 접하게 되며, 자바 개발자라면 반드시 익혀야 할 핵심 개념입니다. 먼저 쓰레드 풀의 개념을 알아보기 전에 쓰레드가 무엇인지 부터 알아보고 왜 쓰레드 풀을 사용하는 지를 알아봐야 합니다.


쓰레드란 무엇일까? 

 

https://www.youtube.com/watch?v=um4rYmQIeRE&t=42s

 

 

우선 프로그램은 실행 가능한 명령어들의 집합이며, 프로세스는 프로그램이 실행되어 메모리와 CPU 자원을 할당받아 동작하는 실행 단위로, 프로그램이 정적인 명령어 집합이라면 프로세스는 이를 실행한 동적인 상태입니다.

 

이를 좀 더 쉽게 말하자면 프로그램은 요리의 레시피처럼 실행 가능한 명령어들의 정적인 집합을 의미합니다. 이 레시피를 실제로 따라 요리를 시작하여 필요한 재료와 도구(메모리와 CPU 자원)를 할당받아 동작하는 실행 단위를 프로세스라고 이해하시면 됩니다.

 

여기서 쓰레드는 요리를 준비하는 여러 명의 요리사로 비유할 수 있습니다. 우선 정의를 하자면 쓰레드는 CPU 코어의 작업 단위라고 할 수 있습니다. 하나의 프로세스는 작업을 쓰레드 단위로 나눌 수 있게되는데 이는 여러 셰프가 동시에 요리를 진행하듯, CPU 코어는 쓰레드 단위로 작업을 병렬적으로 처리합니다.

 

중요한 것은 요리를 한 명이 전부 하는 대신 여러 명의 요리사가 나눠 맡을 수 있는 것처럼, 하나의 프로세스를 쓰레드 단위로 나눌 수 있다는 것입니다. 이를 통해 하나의 프로세스에서 두 가지 이상의 작업을 동시에 실행할 수 있게 됩니다.

 


그렇다면 쓰레드만을 이용해서 동시적으로 작업을 실행할 수 있을까?

 

https://www.youtube.com/watch?v=um4rYmQIeRE&t=42s

 

 

하나의 요청에 하나의 쓰레드가 만들어지고 응답을 준 뒤에 해당 쓰레드를 없애는 방식으로 동시성을 잡아 보았다고 가정해보면 이렇게 할 경우 크게 두 가지의 문제점이 생깁니다.

 


One-to-One Threading-Model

 

자바는 one-to-one 쓰레딩 모델을 사용하기 때문에, 프로세스에서 새로운 쓰레드(User 쓰레드)를 생성할 때마다 OS 쓰레드와 연결됩니다. 이 과정에서 새로운 쓰레드가 생성될 때마다 OS 커널과의 작업이 필요해지며, 그 결과 쓰레드 생성에는 비용이 많이 듭니다.

 

즉, 작업 요청이 들어올 때마다 매번 새로운 쓰레드를 생성하면, 쓰레드 생성 및 OS와의 통신 과정에서 시간이 소모되므로 요청 처리 시간이 늘어날 수밖에 없습니다.

 

실제로 User 쓰레드는 OS 쓰레드를 추상화한 유저 프로그램 계층의 개념입니다. 이는 OS 쓰레드를 직접 다루지 않고, 사용자 관점에서 더 쉽게 관리할 수 있도록 만들어진 추상화라고 볼 수 있습니다. 하지만 내부적으로는 OS 쓰레드와 연결되어 동작하기 때문에, 결국 작업 요청이 들어올 때마다 쓰레드를 생성하면 최종적인 요청 처리 시간이 증가하게 됩니다

 

 

 

 


 

Context-Switching

 

프로세스의 처리 속도보다 빠르게 요청이 계속 들어오면 새로운 쓰레드가 무제한적으로 생성될 수 있습니다. 쓰레드가 많아질수록 메모리를 많이 차지하게 되고, 동시에 컨텍스트 스위칭이 빈번해지면서 성능 문제가 발생할 수 있습니다.

 

컨텍스트 스위칭은 CPU가 여러 쓰레드를 번갈아 가며 실행할 때 발생하는 과정입니다. CPU는 한 쓰레드를 실행하다가 다른 쓰레드로 전환하기 위해, 현재 실행 중인 쓰레드의 상태(레지스터, 프로그램 카운터 등)를 저장하고 새로운 쓰레드의 상태를 로드합니다. 이 작업은 CPU가 실제 작업을 수행하지 않고 쓰레드 간의 전환을 관리하는 데 리소스를 소비하게 만듭니다.

 

쓰레드가 많아질수록 이러한 컨텍스트 스위칭의 빈도가 높아지고, CPU가 전환 작업에 시간을 더 많이 쓰게 되면서 오버헤드(CPU와 메모리의 부하)가 증가합니다. 이는 결국 실제 작업 처리 시간이 줄어들고 시스템 성능이 저하되는 원인이 됩니다.

 


Thread Pool

 

위에서의 문제를 예방하기 위해 쓰레드 풀이란 개념이 도입 되었습니다. 쓰레드 풀은 쓰레드를 허용된 개수 안에서 사용하도록 제한하는 시스템으로 웅덩이에 일정량의 쓰레드를 모아놓고 해당 범위내의 쓰레드만을 이용하게 하는 것을 말합니다.

 

 

 

쓰레드 풀의 내부구조를 보게 되면 쓰레드 풀에서 쓰레드 작업을 할 쓰레드들과 작업 큐가 있습니다. 매커니즘은 쓰레드 풀에 작업 처리 요청이 오게 되면 작업 큐에 작업들이 적재가 되고 쓰레드 풀안에 있는 쓰레드들이 각 작업을 할당받아 작업을 처리를 하게 되는 구조를 지닙니다.

 

쓰레드 풀의 주요 특징 중 하나는 정해진 개수의 쓰레드만을 사용하며, 작업이 완료된 쓰레드는 제거되지 않고 재사용된다는 점입니다.

 

기존 방식에서는 작업 요청이 들어올 때마다 새로운 쓰레드를 생성해야 했고, 이 과정에서 OS 커널과의 연결 작업으로 인해 요청 처리 시간이 증가하고 쓰레드 생성 비용이 커지는 단점이 있었습니다. 하지만 쓰레드 풀은 미리 생성된 쓰레드를 재사용하기 때문에 이러한 비용을 효과적으로 줄일 수 있습니다.

 

또한, 요청이 많아질수록 쓰레드가 무한정 생성되는 기존 구조의 문제를 해결합니다. 쓰레드 풀은 사용할 수 있는 쓰레드의 개수를 제한함으로써 과도한 쓰레드 생성으로 인한 리소스 문제를 방지합니다.

 


Thread Pool의 종류

쓰레드 풀은 다양한 형태로 제공되며, 각기 다른 목적과 상황에 맞게 사용할 수 있습니다. 주요 쓰레드 풀의 종류를 살펴보면 다음과 같습니다. 이제 각각에 대해 알아보겠습니다.

 


Java의 Thread Pool

 

 

 

 

자바에서는 ThreadPoolExecutor 클래스를 이용하여서 자체 쓰레드 풀을 구축하고 있으며 구성 시 몇 가지 특징이 있는 설정 정보들이 있습니다.

 

maximunPoolSize 쓰레드 풀이 가질 수 있는 최대 쓰레드의 갯수
corePoolSize 쓰레드 풀이 가질 수 있는 최소 쓰레드의 갯수
KeepAliveTime 시간에 따라 쓰레드 갯수를 컨트롤 설정

 

 

쓰레드 풀은 maximumPoolSize로 설정된 최대 쓰레드 개수를 유지할 수 있지만, 요청이 줄어들거나 없을 경우 작업을 하지 않는 쓰레드들이 풀에 남아있게 됩니다. 이러한 비활성 쓰레드는 리소스를 차지할 뿐 실제로는 아무런 작업도 하지 않으므로, 메모리와 CPU 자원을 낭비하게 됩니다.

 

이를 방지하기 위해 keepAliveTime 설정이 사용됩니다. 지정된 시간 동안 작업 요청이 없으면, 쓰레드 풀 corePoolSize에 설정된 최소 쓰레드 수만 유지하며 나머지 쓰레드들을 제거합니다. 이렇게 하면, 풀의 리소스를 효율적으로 관리하고 불필요한 자원 낭비를 줄일 수 있습니다.

 


 Tomcat의 Thread Pool

 

 

 

Java의 쓰레드 풀과 구조가 유사하지만 중요한 차이점만을 얘기해보자면, Tomcat 쓰레드 풀에서 Max-ConnectionsAccept-Count는 서버가 동시에 처리할 수 있는 요청과 대기 요청의 한계를 설정하는 중요한 옵션입니다.

 

Max-Connections는 Tomcat이 동시에 처리할 수 있는 최대 연결 개수를 의미합니다. 클라이언트로부터 웹 요청이 들어오면 Tomcat의 Connector가 연결(Connection)을 생성하고, 요청을 Thread Pool의 쓰레드에 연결하여 처리하게 됩니다.

 

한편, Accept-Count는 Max-Connections를 초과하는 요청이 들어왔을 때 사용할 수 있는 대기열(Queue)의 크기를 설정합니다. 대기열의 크기가 Accept-Count를 초과하면 추가 요청은 거절될 수 있습니다.

 

 

 

'CS' 카테고리의 다른 글

스프링 Ioc와 DIP란?  (1) 2025.02.10
트랜잭션 전파 제어  (0) 2025.01.27
HTTPS란?  (0) 2025.01.13
[JPA] 다양한 연관관계 매핑  (1) 2024.12.25
[JPA] 연관관계 매핑 기초  (0) 2024.12.23