Thread Pool 장점
- 스레드 생성 및 소멸 오버헤드 감소
- 미리 생성된 스레드를 유지하며 이러한 오버헤드를 줄인다.
- 작업이 필요할 때마다 새로운 스레드를 생성하는 대신, 기존의 스레드를 재사용한다.
- 리소스 관리 및 성능 향상
- 제한된 수의 스레드를 유지하여 시스템 리소스를 효율적으로 관리
- 과도한 스레드 생성으로 인한 메모리 부족이나 성능 저하를 방지하고, 애플리케이션의 안정성을 높임
- 작업 대기열 관리
- 작업 대기열을 통해 동시에 처리할 수 없는 작업을 관리
- 작업이 도착하는 즉시 처리되지 않더라도, 시스템이 안정적으로 작업을 처리할 수 있도록 도와준다.
Thread Pool 종류
FixedThreadPool
- 고정된 수의 스레드를 유지하며, 일정한 작업량을 처리하는 데 적합
CachedThreadPool
- 작업이 들어올 때마다 새로운 스레드를 생성
- 생성된 스레드가 일정 시간 아무 일을 하지 않으면 해당 스레드 제거
- 자동 회수
- 짧은 시간에 많은 작업을 처리할 때 유용하다.
ScheduledThreadPool
- 지정된 시간 이후나 주기적으로 작업을 실행하는 데 사용
SingleThreadExecutor
로 생성되는 스레드 풀
- 단일 스레드를 사용하여 작업을 순차적으로 처리
- 무제한 큐를 사용한다.
- 동시에 하나의 작업만 실행되어야 하는 경우에 적합
- 단일 스레드를 사용하여 작업을 순차적으로 처리
ThreadPoolTaskExecutor
로 생성되는 스레드 풀- 세부 사항을 직접 설정한 스레드 풀
Thread Pool 설정
CPU와 메모리 사용량 간의 균형을 맞추는 것이 중요하다.
스레드가 많아지면 잠재적으로 더 많은 CPU 코어를 활용할 수 있지만 메모리 사용량이 늘어날 수 있다.
- CPU 바운드 작업
- 작은 큐와 큰 스레드 풀 사용. CPU를 최대한 활용하여 처리 속도를 높임
- I/O 바운드 작업
- 큰 큐와 적당한 스레드 풀 사용. 스레드가 대기 상태일 때 효율적
- 종합 조정
- 큐 크기와 스레드 풀 크기를 적절히 조정하여 작업량과 시스템 리소스에 맞게 최적화
Core Pool Size
코어 풀 크기는 유휴 상태인 경우에도 스레드 풀에서 유지되는 스레드 수를 나타낸다.
이 매개변수는 CPU 및 RAM 사용량에 직접적인 영향을 준다.
코어 풀 크기가 클수록 스레드 풀이 더 많은 동시 작업을 처리할 수 있으므로 잠재적으로 더 많은 CPU 리소스를 활용할 수 있다.
- 적절한 값 선택을 위한 고려 사항
- 작업의 특성과 사용 가능한 리소스
- 작업의 수명이 짧고 빈번한 경우 코어 풀 크기가 클수록 유리할 수 있다.
- 메모리 사용량 증가 가능성을 염두에 두고 애플리케이션의 특성에 맞는 균형을 찾아야 한다.
Maximum Pool Size
최대 풀 크기는 유휴 스레드와 활성 스레드를 모두 포함하여 풀의 스레드 수에 대한 상한을 설정한다.
최대 풀 크기가 클수록 스레드 풀의 용량을 일시적으로 늘려 작업 폭주를 처리할 수 있다.
- 적절한 값 선택을 위한 고려 사항
- 예상되는 최대 부하를 결정하고 그에 따라 최대 풀 크기를 설정
- 너무 높게 설정하면 리소스가 과도하게 소비될 수 있고, 너무 낮게 설정하면 피크 시간대에 작업이 대기열에 올라갈 수 있다.
Task Queue Capacity
작업 대기열은 풀에 사용 가능한 스레드가 없을 때 실행 대기 중인 작업을 보관하며, 코어 풀 크기를 초과하는 작업의 버스트를 처리하는 버퍼 역할을 한다.
작업 대기열이 크면 피크 시간대에 작업을 버퍼링 하여 작업이 거부되는 것을 방지할 수 있다.
- 적절한 값 선택을 위한 고려 사항
- 작업 대기열 용량은 예상 부하와 시스템의 작업 폭주 처리 능력에 따라 달라진다.
- 작업 대기열이 크면 부하가 높을 때 작업이 거부되는 것을 방지할 수 있다.
Thread Pool Queuing
corePoolSize
보다 실행 중인 스레드가 적을 때- 새 스레드를 생성하여 작업을 실행
- 큐에 넣지 않는다.
corePoolSize
이상의 스레드가 실행 중일 때- 작업을 큐에 추가한다.
- 스레드 수는 늘어나지 않는다.
- 큐가 가득 찬 경우
- 새 스레드를 생성하여 작업을 실행
- 스레드 수가
maximumPoolSize
를 초과하면 작업은 거부
Queuing 전략
- 직접 전달
SynchronousQueue
사용- 작업을 큐에 보관하지 않고 바로 실행 가능한 스레드에 즉시 전달
- 스레드가 즉시 실행할 수 없으면 새 스레드 생성
- 장점: 작업 간 의존성이 있는 경우 교착 상태 방지
- 단점: 작업 생성 속도가 처리 속도를 초과하면 스레드 수가 무한히 증가할 가능성
- 무제한 큐
LinkedBlockingQueue
(크기 미지정) 사용.- corePoolSize까지만 스레드 생성, 이후 작업은 큐에 쌓임.
- 장점: 스레드 수는
corePoolSize
로 제한되어 안정적. - 단점: 작업이 계속 쌓이면 큐 크기가 무한히 증가해 메모리 부족 발생 가능.
- 제한된 큐 (Bounded Queue)
ArrayBlockingQueue
(고정 크기 지정) 사용- 큐가 가득 차면 새 스레드 생성,
maximumPoolSize
를 초과하면 작업 거부 - 장점: 자원 고갈 방지
- 단점: 큐 크기와 스레드 풀 크기 조정이 어려워 성능 튜닝 필요
Thread Pool 작업 거부 정책
- Executor가 종료된 경우: 더 이상 작업을 받을 수 없음
- 스레드와 큐의 용량이 한계에 도달한 경우: 새로운 작업을 처리할 수 없음
- 미리 정의된 4가지 핸들러 정책
AbortPolicy
(기본값)
- 작업이 거부되면 RejectedExecutionException 예외를 던짐
- 기본 정책으로, 작업 거부 시 애플리케이션이 예외를 처리하도록 설계
CallerRunsPolicy
- 작업을 제출한 호출 스레드가 직접 작업을 실행
- eg.
main
,exec
- eg.
- 새로운 작업 제출 속도를 줄이는 피드백 메커니즘 제공
- 작업을 제출한 호출 스레드가 직접 작업을 실행
DiscardPolicy
- 실행 불가능한 작업을 조용히 삭제
- 작업 거부 시 아무런 추가 작업도 수행하지 않음
- 주로 작업 누락이 애플리케이션에 치명적이지 않은 경우 사용.
DiscardOldestPolicy
- 큐가 가득 찬 경우
- 큐의 가장 오래된 작업(큐 헤드)을 삭제
- 이후 새로운 작업을 재시도
- 큐가 제한된 용량일 때 적합하며, 오래된 작업보다 최신 작업을 우선시
- 큐가 가득 찬 경우
ThreadPoolExecutor Hook 메서드
ThreadPoolExecutor
는 작업 실행 전후와 스레드 풀 종료 후 특정 작업을 수행하기 위한 Hook 메서드를 제공
이를 통해 실행 환경을 조정하거나 부가적인 처리를 할 수 있다.
beforeExecute(Thread t, Runnable r)
- 작업 실행 직전 호출된다.
- 용도
- 스레드 로컬 데이터 초기화
- 작업 실행 전 로깅 또는 환경 설정
afterExecute(Runnable r, Throwable t)
- 작업 실행 직후 호출된다.
t
는 작업 실행 중 발생한 예외를 나타낸다.- 용도
- 작업 결과 로깅
- 작업 실패 시 예외 처리
- 통계 데이터 수집
terminated()
- 스레드 풀이 완전히 종료된 후 호출된다.
- 용도
- 리소스 정리 및 해제
- 종료 상태 로깅
- 주의사항
- 예외 처리 필요
- 훅 메서드에서 예외가 발생하면 내부 작업자 스레드가 비정상 종료될 수 있음
- 정확한 구현이 요구되며, 스레드 풀의 안정성에 영향을 미칠 수 있음
- 예외 처리 필요
스레드
ThreadPoolExecutor
의 기본 ThreadFactory(Executors.defaultThreadFactory())
는 생성하는 모든 스레드를 비데몬으로 설정한다.
비데몬 스레드는 주요 작업을 처리하는 데 사용되므로 JVM 종료를 방지하는 역할을 한다.
ThreadPoolExecutor
로 데몬 스레드를 생성하기 위해서는 ThreadFactory
를 아래와 같이 정의하면 된다.
ThreadFactory daemonThreadFactory = new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setDaemon(true); // 데몬 스레드로 설정
return thread;
}
};
ExecutorService executor =
new ThreadPoolExecutor(
2, 4, 60L, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(10),
daemonThreadFactory // 사용자 정의 ThreadFactory
);
'자바' 카테고리의 다른 글
세마포어 (0) | 2025.03.06 |
---|---|
Kotlin Data Classes (0) | 2025.03.04 |
스트림 메서드 정리 (0) | 2024.12.19 |
모던 자바 인 액션을 다시 읽으며 든 스트림에 대한 생각 정리 (0) | 2024.12.18 |
Thread와 Lock 관련된 개념 정리 (1) | 2024.09.28 |