핵심 인사이트 (3줄 요약)
- 본질: 스레드 풀 (Thread Pool)은 프로세스 실행 초기나 유휴 시점에 미리 일정한 수의 스레드를 생성하여 컨테이너(풀)에 보관하고, 작업이 요청될 때마다 이들을 재사용하는 자원 관리 패턴이다.
- 가치: 스레드의 잦은 생성과 소멸로 인한 커널 모드 전환 오버헤드를 제거하여 시스템의 응답 속도를 높이고, 동시에 실행될 수 있는 최대 스레드 수를 제한함으로써 과부하 시 시스템 자원 고갈(OOM, Out of Memory)을 방지한다.
- 융합: 현대의 대규모 웹 서버 (Apache, Nginx, Tomcat 등), 데이터베이스 커넥션 관리, 그리고 언어 수준의 비동기 런타임(Java ExecutorService 등)에서 요청 병목을 조절하고 예측 가능한 레이턴시(Latency)를 보장하는 필수 기반 아키텍처다.
Ⅰ. 개요 및 필요성 (Context & Necessity)
- 개념: 스레드 풀 (Thread Pool)은 다수의 요청을 병렬로 처리하기 위해, 작업(Task)을 실행할 워커 스레드 (Worker Thread) 집합을 미리 스폰(Spawn)하여 대기 상태로 유지하고 작업 큐(Task Queue)를 통해 작업을 분배하는 소프트웨어 디자인 패턴이다.
- 필요성: 웹 서버에 초당 수천 건의 접속 요청이 들어올 때, 매 요청마다 스레드를 새로 생성(
Thread.start())하고 완료 후 소멸시키면 시스템의 CPU (Central Processing Unit) 시간 대부분이 비즈니스 로직 처리가 아닌 스레드 생명주기 관리에 낭비된다. 또한, 악의적이거나 돌발적인 트래픽 폭증(Traffic Spike) 시 수만 개의 스레드가 동시 생성되면 커널 메모리가 고갈되고 문맥 교환 (Context Switching) 비용이 기하급수적으로 증가하여 시스템이 다운(Thrashing)되는 심각한 취약점이 발생한다. 이를 방어하고 안정적인 처리량을 유지하기 위해 스레드 재사용과 동시성 제한 메커니즘이 절대적으로 필요하다. - 💡 비유: 대형 마트의 계산대 직원이 손님이 올 때마다 집에서 출근하고 계산이 끝나면 바로 퇴근하는 대신, 미리 정해진 5명의 직원이 계산대에 상주(스레드 풀)하며 대기줄(작업 큐)의 손님을 순서대로 처리하는 시스템과 같다.
- 등장 배경: 초기 클라이언트-서버 모델에서는 "요청당 스레드 (Thread-per-request)" 방식이 주류였다. 그러나 인터넷의 보급으로 C10K (Concurrent 10,000 connections) 문제가 대두되면서, 무한정한 스레드 생성은 운영체제 아키텍처상 한계가 명확함이 증명되었다. 이에 따라 작업(Task)과 실행체(Thread)를 분리하여 자원을 통제하는 스레드 풀 개념이 서버 아키텍처의 표준으로 자리 잡았다.
요청당 스레드 생성 방식과 스레드 풀 방식의 시스템 자원 파괴 및 보호 메커니즘을 비교 시각화하면 스레드 풀의 방어적 성격이 명확해진다.
┌──────────────────────────────────────────────────────────────────────────┐
│ 요청당 스레드 생성(Before) vs 스레드 풀 적용(After) │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ [방식 A: 요청당 스레드 직접 생성 (Thread-per-request)] │
│ 요청 폭주 (1,000 req/sec) ─────▶ 1,000개의 스레드 동시 생성 시도 │
│ │ │
│ ▼ │
│ ┌───────────────────────────┐ │
│ │ CPU 문맥 교환 폭증 (Thrashing)│ │
│ │ 커널 메모리 OOM 발생 │ ◀─ 시스템 다운! │
│ └───────────────────────────┘ │
│ │
│ [방식 B: 스레드 풀 적용 (Thread Pool Pattern)] │
│ 요청 폭주 (1,000 req/sec) ─────▶ [작업 대기 큐 (Task Queue)] │
│ │ (메모리에 안전하게 적재) │
│ ▼ │
│ ┌───────────────────────────┐ │
│ 순차적 꺼내오기 │ 스레드 풀 (Max 100개) │ │
│ (재사용) │ [T1] [T2] [T3] ... [T100] │ ◀─ 안정적 처리! │
│ └───────────────────────────┘ │
│ 시스템 자원 보호 및 예측 가능한 레이턴시 보장 │
└──────────────────────────────────────────────────────────────────────────┘
[다이어그램 해설] 상단의 "요청당 스레드" 모델은 트래픽과 시스템 부하가 1:1로 결합된 매우 위험한 아키텍처다. 클라이언트의 접속 요청이 몰리면 운영체제는 수천 개의 커널 스레드를 스폰하려 시도하며, 이 과정에서 커널 공간의 TCB (Thread Control Block) 할당 오버헤드와 수많은 스레드 간의 경쟁으로 인해 CPU는 문맥 교환(Context Switching)에만 리소스를 소모하는 스래싱(Thrashing) 상태에 빠진다. 하단의 "스레드 풀" 모델은 외부 트래픽의 변동성을 '작업 대기 큐'라는 버퍼(Buffer)를 통해 흡수하고, 오직 사전에 계산된 안전한 수(예: 100개)의 스레드만이 시스템에서 동시에 구동되도록 물리적 한계선(Cap)을 설정한다. 처리 속도는 요청 유입 속도를 따라가지 못해 일부 지연이 발생할 수 있으나, 서버가 다운되지 않고 최대 처리량(Throughput)을 꾸준히 유지할 수 있다는 점에서 엔터프라이즈 환경의 필수 생존 전략이다.
- 📢 섹션 요약 비유: 댐이 없이 강물이 불어나는 대로 마을로 흘려보내면 홍수(시스템 다운)가 나지만, 거대한 저수지(작업 큐)에 물을 가둬두고 수문(스레드 풀)을 통해 정해진 양만 안전하게 방류하여 수력 발전(비즈니스 로직)을 안정적으로 유지하는 치수 시설과 같습니다.
Ⅱ. 아키텍처 및 핵심 원리 (Deep Dive)
구성 요소
| 요소명 | 역할 | 내부 동작 | 관련 기술 | 비유 |
|---|---|---|---|---|
| 작업 큐 (Task Queue) | 외부에서 들어온 요청(Task)을 순서대로 임시 보관 | 스레드 안전(Thread-safe)한 블로킹 큐(Blocking Queue)로 구현 | FIFO Queue | 은행의 계산 대기줄 |
| 워커 스레드 (Worker Thread) | 실제 작업을 수행하는 무한 루프 스레드 | 큐에서 작업을 take()로 꺼내어 run() 실행 후 다시 대기 | Runnable, Callable | 계산대 상주 직원 |
| 스레드 풀 관리자 (Pool Manager) | 스레드의 생성, 유지, 파기 및 작업 스케줄링 총괄 | Core/Max 사이즈 설정, Keep-alive 시간 관리 | Java ExecutorService | 지점장 (인력 스케줄러) |
| 거절 정책 (Rejection Policy) | 큐가 가득 찼을 때 들어오는 새 요청 처리 방법 | 예외 발생, 호출자 실행, 조용한 폐기 등 결정 | RejectedExecutionHandler | 입장 인원 초과 시 안내판 |
스레드 풀의 내부 아키텍처는 생산자-소비자 패턴 (Producer-Consumer Pattern)의 완벽한 구현체다. 외부 클라이언트는 생산자로서 작업을 큐에 밀어 넣고, 워커 스레드들은 소비자로서 큐에서 작업을 꺼내어 처리한다.
┌────────────────────────────────────────────────────────────────────────────┐
│ 스레드 풀 기반 비동기 처리 아키텍처 상세도 │
├────────────────────────────────────────────────────────────────────────────┤
│ │
│ [Client / Main Thread] │
│ Task 1, Task 2 ... Task N │
│ │ │
│ │ submit() 또는 execute() │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ Thread Pool Manager │ │
│ │ │ │
│ │ ┌───────────────────────┐ ┌───────────────────────┐ │ │
│ │ │ Task Queue (Blocking) │ │ Worker Threads (Pool) │ │ │
│ │ │ │ │ │ │ │
│ │ │ [ T4 ] [ T3 ] [ T2 ] │ ◀──take │ [ WT 1 (Idle) ] │ │ │
│ │ │ │ │ [ WT 2 (Running)] │ │ │
│ │ └───────────────────────┘ │ [ WT 3 (Running)] │ │ │
│ │ └───────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
│ * WT 2 내부 동작: while(true) { Task t = Queue.take(); t.run(); } │
│ * Queue가 비어있으면 WT 1은 Condition Variable에서 블로킹 대기(Idle) │
└────────────────────────────────────────────────────────────────────────────┘
[다이어그램 해설] 이 구조도의 핵심은 클라이언트(Main Thread)와 작업자(Worker Thread) 사이를 완벽히 디커플링(Decoupling)한 점이다. 클라이언트가 submit() API를 호출하면 작업 객체(Runnable/Callable)가 Thread-safe한 Task Queue에 삽입되고 즉시 리턴된다. 반대편에서 Worker Thread들은 무한 루프(while(true))를 돌며 큐의 take() 메서드를 호출한다. 만약 큐가 비어있다면 워커는 루프를 돌며 CPU를 소모하는 것이 아니라, 내부적으로 모니터 락(Monitor Lock)의 조건 변수(Condition Variable)에서 잠든(Idle) 상태가 된다. 새로운 작업이 큐에 들어오면 큐가 워커 스레드 중 하나를 깨워(Wake-up) 작업을 전달한다. 이처럼 스레드의 라이프사이클을 생성/소멸이 아닌 '대기(Sleep)와 깨어남(Wake)' 상태 전이로 대체함으로써 생성 오버헤드를 제로(0)에 가깝게 만든다.
심층 동작 원리: 스레드 풀의 크기 팽창과 수축
최신 스레드 풀(예: Java의 ThreadPoolExecutor)은 고정된 크기만 갖지 않고 트래픽에 따라 동적으로 탄력성(Elasticity)을 가진다.
① Core Pool Size: 초기에 유지되는 기본 스레드 개수. 작업이 없어도 이 수만큼은 스레드를 살려둔다.
② Queue Capacity: 기본 스레드가 모두 바쁠 때, 대기할 수 있는 작업의 최대 한도.
③ Maximum Pool Size: 큐마저 가득 찼을 때, 최후의 수단으로 임시 확장할 수 있는 스레드의 최대 한도.
④ Keep-Alive Time: 임시로 확장된 스레드가 작업을 끝내고 대기할 때, 이 시간이 지나면 메모리 확보를 위해 스스로 종료한다.
- 📢 섹션 요약 비유: 식당에 평소(Core Size)에는 홀서빙 직원 3명을 유지하다가, 손님 대기명단(Queue)마저 꽉 차면 임시 아르바이트생(Max Size)을 급히 부르고, 점심시간이 지나 한가해지면(Keep-alive time) 아르바이트생을 퇴근시켜 인건비를 아끼는 스마트 매장 관리와 같습니다.
Ⅲ. 융합 비교 및 다각도 분석
스레드 풀 크기 튜닝: 정량적 최적화 이론
스레드 풀 아키텍처에서 가장 어려운 기술사적 과제는 "풀의 크기(N)를 몇 개로 설정할 것인가?"이다. 너무 적으면 CPU 코어가 놀고 병목이 발생하며, 너무 많으면 문맥 교환 비용으로 성능이 수직 낙하한다. 작업의 성격(CPU 바운드 vs I/O 바운드)에 따라 최적의 공식이 다르다.
| 워크로드 특성 | 병목 지점 | 적정 스레드 수 공식 (일반론) | 설명 및 근거 |
|---|---|---|---|
| CPU 바운드 | 프로세서 연산 능력 | N_threads = N_cpu + 1 | 락(Lock)이나 I/O 대기가 없으므로 코어 수만큼만 스레드가 도는 것이 캐시 미스와 컨텍스트 스위칭 최소화에 유리. +1은 페이지 폴트 시 여분. |
| I/O 바운드 | 디스크/네트워크 지연 | N_threads = N_cpu * (1 + Wait/Compute 시간 비율) | 스레드가 I/O 대기(Wait) 상태일 때 CPU가 쉬지 않도록 더 많은 스레드를 투입해야 함. DB 쿼리나 API 호출이 많을수록 풀 크기를 수십~수백 개로 키움. |
이러한 수학적 최적화 공식을 바탕으로 스레드 풀 크기에 따른 시스템 처리량(Throughput)의 변화를 그래프로 시각화하면, 성능 변곡점을 명확히 이해할 수 있다.
┌────────────────────────────────────────────────────────────────────────────┐
│ 스레드 풀 크기(Pool Size)에 따른 처리량(Throughput) 변화 │
├────────────────────────────────────────────────────────────────────────────┤
│ │
│ 처리량 (TPS) │
│ ▲ [최적점: Optimal Size] │
│ │ ★ │
│ │ / \ (문맥 교환 오버헤드 증가) │
│ │ / \ │
│ │ / \ │
│ │ / \ │
│ │ / (자원 낭비 및 메모리 고갈 영역) │
│ │ / \ │
│ │ / \ │
│ │/ (스레드 부족으로 병목) \ │
│ └─────────────────────────────────────────────▶ 스레드 풀 크기 │
│ | N_cpu | | N_cpu * (1+W/C) | │
│ (CPU 바운드 최적) (I/O 바운드 최적) │
│ │
│ 분석: 무작정 스레드 풀을 키우는 것은 독이다. 정점을 지나면 스위칭 비용이 │
│ 실제 연산 시간보다 커져 전체 TPS가 오히려 떨어지는 '역전 현상' 발생.│
└────────────────────────────────────────────────────────────────────────────┘
[다이어그램 해설] 이 그래프는 서버 튜닝에서 가장 중요한 리틀의 법칙(Little's Law)과 암달의 법칙(Amdahl's Law)이 융합된 결과를 보여준다. 스레드 수가 하드웨어 코어 수보다 적을 때는 병렬성이 100% 활용되지 않아 처리량(TPS, Transactions Per Second)이 직선으로 상승한다. 그러나 작업이 I/O 바운드 특성을 가질 경우, 코어 수보다 더 많은 스레드를 투입해 최적점(Optimal Size)까지 도달해야 CPU 사용률을 극한까지 끌어올릴 수 있다. 핵심은 별표(★) 지점을 넘어서 스레드 풀을 과도하게(예: 10,000개) 설정했을 때 일어나는 현상이다. 커널 스케줄러가 수만 개의 스레드를 번갈아 실행하기 위해 CPU 레지스터를 교체(Context Switch)하는 데 시간을 다 써버려, 정작 유효한 비즈니스 로직을 처리하는 시간은 급감하게 된다. 이를 '스래싱(Thrashing)'이라 하며, 실무에서 시스템 부하 테스트(Load Testing)를 통해 이 최적점을 반드시 찾아야 하는 이유가 여기에 있다.
- 📢 섹션 요약 비유: 고속도로의 차선(CPU 코어)은 4개인데 진입 차량(스레드)을 무한정 늘리면 결국 심각한 교통체증(문맥 교환 오버헤드)으로 아무도 앞으로 가지 못하는 주차장으로 변하는 원리와 같습니다.
Ⅳ. 실무 적용 및 기술사적 판단
실무 시나리오
-
시나리오 — 장애 전파 방지를 위한 스레드 풀 격리 (Bulkhead Pattern): 마이크로서비스(MSA) 환경의 주문 서버가 '결제 API'와 '알림 API'를 호출하고 있다. 어느 날 알림 서버에 장애가 나 응답 지연이 무한정 길어지자, 주문 서버의 모든 스레드 풀이 알림 API 응답을 대기하느라 블로킹되었고, 정작 정상인 결제 API조차 호출하지 못해 주문 서버 전체가 뻗어버렸다. 아키텍트는 서킷 브레이커(Circuit Breaker)를 도입하고, 결제용 스레드 풀과 알림용 스레드 풀을 물리적으로 분리(격리)하여, 알림 스레드 풀이 고갈되더라도 결제는 정상 진행되도록 시스템 복원력(Resilience)을 확보했다.
-
시나리오 — 메모 고갈 (OOM) 대응: Java Spring Boot 웹 서버에서 기본 설정인
Unbounded Queue(무제한 큐)를 사용하다가 트래픽 폭주 시 대기 큐에 수백만 개의 Task 객체가 쌓이면서 JVM (Java Virtual Machine) Heap 메모리 OOM이 발생해 서버가 비정상 종료되었다. 문제를 인지한 엔지니어는 큐 사이즈를 5,000개로 고정(Bounded Queue)하고, 초과 시CallerRunsPolicy(요청을 보낸 메인 스레드가 직접 작업을 실행하도록 하여 유입 속도를 늦추는 백프레셔 효과) 거절 정책을 적용하여 서버 붕괴를 원천 방어했다.
실무에서 발생할 수 있는 스레드 풀의 큐 적체(Queue Accumulation) 상황에서 거절 정책(Rejection Policy)을 어떻게 설정해야 하는지 흐름도로 판단해 본다.
┌───────────────────────────────────────────────────────────────────────────┐
│ 스레드 풀 포화 상태 시나리오 및 거절 정책(Rejection) 판단 플로우 │
├───────────────────────────────────────────────────────────────────────────┤
│ │
│ [Task Queue 가득 참 (Capacity Reached)] │
│ │ │
│ ▼ │
│ 유실되면 안 되는 중요한 비즈니스 데이터인가? │
│ ├─ 예 ──▶ 클라이언트가 지연을 견딜 수 있는가? │
│ │ ├─ 예 ──▶ [CallerRunsPolicy] 적용 │
│ │ │ (호출 스레드가 직접 실행 → 백프레셔) │
│ │ └─ 아니오 ─▶ [AbortPolicy] 예외 발생 후, │
│ │ Kafka/RabbitMQ 등 영구 큐로 재전송 │
│ │ │
│ └─ 아니오 (로그, 통계 핑, 오래된 캐시 갱신 등) │
│ │ │
│ ▼ │
│ 최신 데이터만 의미가 있는가? │
│ ├─ 예 ──▶ [DiscardOldestPolicy] 적용 │
│ │ (큐의 맨 앞(가장 오래된) 작업 버리고 새 작업 넣음) │
│ └─ 아니오 ─▶ [DiscardPolicy] 적용 │
│ (조용히 새 작업을 무시함) │
│ │
│ 💡 핵심 판단: 무제한 큐(Unbounded)는 절대 실무에서 사용하면 안 된다. │
│ 반드시 Bounded Queue + 적절한 Rejection 전략을 쌍으로 구성하라. │
└───────────────────────────────────────────────────────────────────────────┘
[다이어그램 해설] 이 의사결정 플로우는 트래픽 폭주라는 전시 상황에서 시스템이 우아하게 성능을 저하시키는(Graceful Degradation) 설계 기법을 다룬다. 큐가 가득 찼다는 것은 서버 처리 능력의 한계에 도달했다는 의미다. 이때 AbortPolicy(기본값)는 에러를 던져 클라이언트에게 '나 바쁘니 나중에 다시 와'라고 명확히 거절하는 가장 안전한 방법이다. CallerRunsPolicy는 생산자(호출자)의 스레드를 강제로 노동에 동원함으로써, 생산자가 새로운 작업을 밀어 넣는 속도를 물리적으로 지연(Back-pressure)시키는 매우 고급스럽고 안정적인 기법이다. 중요도가 낮은 로그 전송 같은 작업은 과감히 버리는 Discard 계열 정책을 써서 핵심 비즈니스 로직의 스레드 풀을 보호해야 한다. 이 판단이 마이크로서비스의 생사를 가르는 아키텍트의 핵심 역량이다.
도입 체크리스트
- 기술적: 코어 스레드 수와 Max 스레드 수가 워크로드의 CPU/IO 특성에 맞게 벤치마킹을 거쳐 튜닝되었는가? 공유 자원에 접근할 때 데드락을 유발할 수 있는 '큐 내부 대기' 구조가 없는가?
- 운영·보안적: 무제한 크기의 작업 대기 큐(예:
LinkedBlockingQueue기본 생성자)를 사용해 OOM 폭탄을 안고 있지는 않은가? 스레드 풀의 활성 스레드 수, 큐 적재량을 APM (Application Performance Monitoring)을 통해 실시간 모니터링하고 알람을 설정했는가?
안티패턴
-
ThreadLocal 누수 (Leak): 스레드 풀 환경에서 사용자 인증 정보 등을 담기 위해
ThreadLocal변수를 사용한 후, 응답 반환 전에 명시적으로remove()하지 않는 경우. 워커 스레드는 소멸되지 않고 재사용되므로, 다음 요청을 처리할 때 이전 사용자의 인증 정보가 그대로 남아 치명적인 보안 사고(권한 탈취)로 이어진다. -
📢 섹션 요약 비유: 만원 지하철에서 더 이상 사람을 밀어 넣지 않고(무제한 큐 방지), 출입문을 닫아 대기시키거나 다음 열차를 타게 안내하는 것(거절 정책)이 모두의 안전을 보장하는 유일한 관제 시스템인 것과 같습니다.
Ⅴ. 기대효과 및 결론
정량/정성 기대효과
| 구분 | 스레드 풀 미적용 (요청당 생성) | 스레드 풀 적용 (적정 튜닝) | 개선 효과 |
|---|---|---|---|
| 정량 | 초당 수천 번의 스레드 생성/소멸 | 스레드 객체 재사용 | 스레드 생성 오버헤드 99% 제거 |
| 정량 | 스파이크 시 커널 메모리 OOM | Max Size 제한으로 자원 방어 | 예측 불가능한 서버 다운타임 0% (안정성 보장) |
| 정성 | 시스템 부하를 예측하기 어려움 | 스레드 풀 분리를 통한 장애 격리 | MSA 환경에서 시스템 복원력(Resilience) 대폭 향상 |
미래 전망
- 비동기 I/O와 이벤트 루프 (Event Loop)로의 진화: I/O가 극도로 많은 현대 웹 환경에서는 수백 개의 스레드 풀 조차도 메모리 낭비로 여겨진다. Nginx, Node.js, Netty 등은 극소수(코어 수)의 스레드와 비동기 논블로킹 I/O (epoll, kqueue)를 결합한 이벤트 루프 아키텍처로 진화하여, 스레드 풀의 문맥 교환 오버헤드마저 없애고 있다.
- 가상 스레드 (Virtual Threads): Java 21에 정식 도입된 Project Loom의 가상 스레드는 OS 커널 스레드와 분리된 매우 가벼운 사용자 수준 스레드를 제공한다. 가상 스레드는 블로킹 시 OS 스레드를 놓아주므로 수백만 개를 생성해도 안전하여, 향후 전통적인 스레드 풀 패턴 자체를 불필요하게 만들 게임 체인저로 주목받고 있다.
참고 표준
- Java
java.util.concurrent패키지:ExecutorService,ThreadPoolExecutor등 엔터프라이즈 환경 스레드 풀의 사실상(De-facto) 표준 구현체 아키텍처. - C++17/20 STL: 비동기 처리를 위한
std::async및 병렬 알고리즘 표준.
스레드 풀은 시스템의 자원이 무한하지 않다는 현실 세계의 물리적 법칙을 소프트웨어적으로 우아하게 제어한 걸작이다. 수십 년간 고성능 서버의 심장부로 활약해 왔으며, 최근 이벤트 루프나 가상 스레드와 같은 새로운 패러다임이 등장하고 있음에도 불구하고 "동시성을 어떻게 통제하고 자원을 보호할 것인가"라는 근본적인 아키텍처 철학은 여전히 모든 시스템 설계의 바이블로 남아있다.
- 📢 섹션 요약 비유: 엔진의 피스톤(스레드 풀)이 연료(작업)를 폭발시킬 때, 튼튼한 엔진 블록(제한 정책)으로 폭발력을 감싸주지 않으면 엔진이 터져버리듯이, 한계와 제약 속에서 최대의 성능을 끌어내는 가장 정교한 제어 공학의 결정체입니다.
📌 관련 개념 맵 (Knowledge Graph)
| 개념 명칭 | 관계 및 시너지 설명 |
|---|---|
| 문맥 교환 (Context Switching) | 스레드 풀을 적절히 제한하지 않았을 때 발생하는 가장 치명적인 CPU 성능 저하 원인이다. |
| 벌크헤드 패턴 (Bulkhead Pattern) | 도메인 또는 타겟 API별로 스레드 풀을 물리적으로 분리하여 특정 서비스 장애가 전체 시스템으로 전파되는 것을 막는 아키텍처 패턴이다. |
| 블로킹 큐 (Blocking Queue) | 생산자(요청)와 소비자(워커 스레드) 간의 속도 차이를 안전하게 버퍼링하고 스레드 안전성을 보장하는 핵심 자료구조다. |
| 리틀의 법칙 (Little's Law) | L = λW (대기열 길이 = 도착률 × 대기시간)를 통해 안정적인 상태의 시스템에서 큐 사이즈와 스레드 풀 크기의 수학적 관계를 증명한다. |
| ThreadLocal | 워커 스레드가 재사용되는 스레드 풀 환경에서 사용 후 초기화하지 않으면 메모리 누수와 데이터 오염을 유발하는 안티패턴의 주범이 된다. |
| 가상 스레드 (Virtual Thread / Goroutine) | 물리적 스레드 풀의 제약을 극복하기 위해 사용자 공간에서 수백만 개의 비동기 워크로드를 저비용으로 스케줄링하는 차세대 스레드 모델이다. |
👶 어린이를 위한 3줄 비유 설명
- 택배 회사가 물건(작업)이 올 때마다 트럭(스레드)을 새로 사서 배달을 보내면 회사가 망해버릴 거예요.
- 그래서 택배 회사는 미리 10대의 트럭을 사두고, 배달을 마친 트럭이 돌아오면 다음 물건을 싣고 또 출발하게 만들었어요. 이게 바로 '스레드 풀'이에요!
- 물건이 너무 많이 오면 창고(작업 큐)에 잠깐 쌓아두고 트럭 10대가 부지런히 돌며 배달하니까, 회사도 안전하고 택배도 확실하게 도착할 수 있답니다.