자바 스레드 풀의 원리와 활용

스레드 풀이란?

스레드 풀은 일종의 작업 스레드 관리 컨테이너로 이해할 수 있다. 애플리케이션이 시작될 때 미리 여러 개의 스레드를 생성하여 풀 안에 보관하고, 작업이 필요할 때마다 새로 스레드를 만들지 않고 이 풀에서 꺼내 사용한다. 작업이 끝나면 스레드는 다시 풀로 반환되어 다음 작업을 기다린다.

기존 방식의 문제점

자바 1.5 이전에는 각각의 작업마다 new Thread()를 통해 새 스레드를 생성해야 했다. 작업 완료 후 해당 스레드는 소멸되므로, 반복적인 생성과 소멸 과정에서 상당한 리소스 오버헤드가 발생했다. 이는 특히 고빈도로 작업이 발생하는 환경에서 성능 저하의 주요 원인이 되었다.

JDK 1.5부터 java.util.concurrent 패키지는 스레드 풀 기반의 동시성 처리를 지원하게 되었으며, 이를 통해 스레드 자원을 효율적으로 재사용할 수 있게 되었다.

스레드 풀 사용의 장점

  • 성능 향상: 스레드 생성 및 종료 비용을 줄여 응답 속도를 개선한다.
  • 자원 최적화: 제한된 스레드 수로 시스템 자원을 효율적으로 관리한다.
  • 중앙 집중식 관리: 스레드 생명주기와 작업 큐를 통합 관리할 수 있다.

스레드 풀 생성 방법

자바에서는 Executors 유틸리티 클래스를 제공하지만, 알리바바 코딩 가이드라인 등에서는 더 명시적인 제어를 위해 ThreadPoolExecutor 직접 생성을 권장한다.

public class Task implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " - 작업 실행 중");
    }
}
public class ThreadPoolExample {
    public static void main(String[] args) {
        // ThreadPoolExecutor 직접 생성
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            3,                    // 핵심 스레드 수 (corePoolSize)
            8,                    // 최대 스레드 수 (maximumPoolSize)
            60L,                  // 유휴 시간 (keepAliveTime)
            TimeUnit.SECONDS,     // 시간 단위
            new LinkedBlockingQueue<>(10) // 작업 큐
        );

        // 12개의 작업 제출
        for (int i = 0; i < 12; i++) {
            executor.execute(new Task());
        }

        executor.shutdown(); // 정상 종료 요청
    }
}

ThreadPoolExecutor 주요 파라미터 설명

corePoolSize – 핵심 스레드 수

풀 내 상시 유지되는 최소 스레드 수다. 새로운 작업이 들어오면 우선 이 수준까지는 새 스레드를 생성하여 즉시 처리한다. 기본 설정에서는 이 수치 이하의 스레드는 시간이 지나도 종료되지 않는다.

maximumPoolSize – 최대 스레드 한도

풀이 동적으로 생성할 수 있는 전체 스레드의 최대치다. 작업 큐가 가득 찬 후에도 남은 작업이 있으면 이 한도까지 추가 스레드를 생성해 처리한다.

keepAliveTime – 유휴 시간

핵심 수(corePoolSize)를 초과한 임시 스레드가 작업 없이 대기할 수 있는 시간이다. 이 시간이 지나면 해당 스레드는 종료된다. allowCoreThreadTimeOut(true) 설정 시 핵심 스레드에도 적용 가능하다.

workQueue – 작업 대기 큐

현재 사용 가능한 스레드가 없을 경우, 새 작업은 이 큐에 저장되어 실행 가능 상태가 될 때까지 대기한다. 대표적인 큐 구현체로는 ArrayBlockingQueue, LinkedBlockingQueue, SynchronousQueue 등이 있다.

작업 처리 흐름 요약

  1. 새 작업 제출
  2. 현재 활성 스레드 수가 corePoolSize보다 작으면 새 스레드 생성 후 실행
  3. 그렇지 않으면 작업 큐에 삽입
  4. 큐가 가득 찼고, 현재 스레드 수가 maximumPoolSize 미만이면 임시 스레드 생성
  5. 모두 포화 상태라면 거부 전략(Rejection Policy)에 따라 처리

이러한 메커니즘은 시스템 부하에 유연하게 대응하면서도 자원 낭비를 방지하는 데 효과적이다. 다음에는 다양한 거부 전략에 대해 살펴볼 예정이다.

태그: 자바 스레드풀 ThreadPoolExecutor 동시성 멀티스레딩

7월 6일 22:41에 게시됨