본문 바로가기

Programming/Java

쓰레드 풀(Thread Pool)의 개념과 최적화 방법

반응형

멀티스레드 프로그래밍에서 성능과 안정성을 확보하기 위해서는 쓰레드 풀(Thread Pool)을 적절하게 구성하고 운영하는 것이 중요하다. 이 글에서는 쓰레드 풀의 기본 개념부터, 구성 요소, 그리고 실무에서 고려해야 할 최적화 방법까지 단계적으로 알아보자.


1. 쓰레드 풀(Thread Pool)이란?

쓰레드 풀은 작업을 처리할 수 있는 일정 수의 쓰레드를 미리 만들어 두고, 들어오는 작업들을 이 풀에 있는 쓰레드로 처리하는 구조이다. 새로운 작업이 들어오면 쓰레드 풀에서 유휴 상태의 쓰레드를 할당해 실행하고, 작업이 끝난 후에는 해당 쓰레드를 다시 풀에 반환하여 재사용한다.

주요 목적

  • 자원 낭비 방지: 매번 스레드를 생성하고 소멸시키는 비용을 줄인다.
  • 성능 향상: 이미 생성된 쓰레드를 재사용함으로써 응답 속도를 개선한다.
  • 자원 제어: 생성 가능한 스레드 수를 제한함으로써 과도한 자원 소비를 방지한다.

2. 쓰레드 풀의 구성 요소

Java의 ThreadPoolExecutor를 기준으로 설명하면, 쓰레드 풀은 다음과 같은 요소들로 구성된다.

  • Core Pool Size: 항상 유지되는 기본 스레드 수
  • Maximum Pool Size: 필요 시 확장 가능한 최대 스레드 수
  • Keep Alive Time: 최대 풀 사이즈에서 줄어들 때까지 스레드가 유휴 상태로 유지되는 시간
  • Work Queue: 작업이 대기하는 큐
  • Thread Factory: 새로운 스레드를 생성하는 팩토리
  • RejectedExecutionHandler: 큐가 가득 찼을 때 작업을 어떻게 처리할지 정의

3. 쓰레드 풀 최적화 전략

3.1 적절한 풀 크기 설정

쓰레드 풀의 크기는 작업의 특성에 따라 다르게 설정해야 한다.

  • CPU 바운드 작업: CPU 코어 수만큼 설정
int coreCount = Runtime.getRuntime().availableProcessors();
  • IO 바운드 작업: 코어 수보다 크게 설정. 일반적으로 coreCount * 2 또는 그 이상까지 허용.

작업량과 처리 시간, 컨텍스트 스위칭 비용 등을 고려하여 적절한 수치를 실험적으로 조정하는 것이 좋다.

 

3.2 적절한 큐 선택

작업 큐는 쓰레드 풀의 동작에 큰 영향을 미친다.

  • LinkedBlockingQueue: 크기 제한이 없을 경우 무제한 대기열로 동작. 메모리 고갈 위험 있음.
  • ArrayBlockingQueue: 용량이 제한된 배열 기반 큐. 리소스 제어에 유리.
  • SynchronousQueue: 큐 없이 작업을 바로 스레드에 전달. 최대 성능이 필요한 경우에 적합.

실무에서는 보통 ArrayBlockingQueue와 같은 bounded queue 사용을 권장한다.

 

3.3 RejectedExecutionHandler 설정

작업 큐가 가득 차거나, 스레드 풀이 가득 찬 상황에서 어떻게 대처할지를 정의해야 한다. 대표적인 전략은 다음과 같다.

  • AbortPolicy: 예외를 던져 작업 거부
  • CallerRunsPolicy: 현재 호출한 쓰레드에서 작업 실행
  • DiscardPolicy: 작업을 무시하고 삭제
  • DiscardOldestPolicy: 가장 오래된 대기 작업을 제거하고 현재 작업 추가

업무 중요도에 따라 적절한 정책을 선택해야 한다.

 

3.4 유휴 스레드 정리

유휴 상태의 스레드를 일정 시간 이후 정리함으로써 리소스를 절약할 수 있다. 이 설정은 특히 트래픽이 불규칙한 시스템에서 유용하다.

threadPoolExecutor.allowCoreThreadTimeOut(true);

4. 주의 사항 및 모니터링

  • 쓰레드 풀 설정은 시스템 환경, 트래픽 패턴, 작업 특성에 따라 달라지므로 초기 설정 이후에도 지속적인 모니터링과 조정이 필요하다.
  • ThreadPoolExecutor의 getPoolSize(), getActiveCount(), getQueue().size() 등을 통해 현재 상태를 확인할 수 있으며, 메트릭 시스템과 연동해 모니터링하는 것이 권장된다.

쓰레드 풀은 단순한 유틸리티 클래스가 아니라, 시스템 성능과 안정성을 결정짓는 핵심 구성 요소 중 하나다. 무조건 많은 쓰레드를 사용하는 것이 아니라, 작업 특성과 시스템 자원을 고려해 최적의 구성을 찾고, 실패 상황을 방어할 수 있도록 설계하는 것이 중요하다.

반응형