Java의 ThreadPoolExecutor를 활용하여 Singleton 패턴 기반의 커스텀 스레드 풀을 구현하고, 여러 개의 작업을 스레드 풀에서 실행하는 예제입니다. 

 

CustomThreadPool 클래스는 Java의 ThreadPoolExecutor를 활용하여 최소/최대 스레드 개수, 대기 큐 크기 및 스레드 유지 시간을 설정한 후, 단일 인스턴스로 관리하는 Singleton 패턴을 적용한 스레드 풀입니다.

 

● 실행 흐름 :

  1. 최대 2개의 스레드가 즉시 실행됩니다.
  2. 이후 최대 5개의 작업이 대기 큐에 쌓입니다.
  3. 대기 큐가 가득 차면 새로운 스레드를 생성하여 실행합니다. (최대 10개)
  4. 10개의 작업이 순차적으로 실행된 후 스레드 풀이 종료됩니다.
import java.util.concurrent.*;

public enum CustomThreadPool {
    INSTANCE;

    private static final int CORE_POOL_SIZE = 2;   // 최소 스레드 개수
    private static final int MAX_POOL_SIZE = 10;   // 최대 스레드 개수
    private static final int QUEUE_CAPACITY = 5;   // 대기 큐 크기
    private static final long KEEP_ALIVE_TIME = 60L; // 유휴 스레드 유지 시간 (초)

    private final ThreadPoolExecutor executor;

    // enum에서 생성자를 사용하여 인스턴스를 초기화
    CustomThreadPool() {
        this.executor = new ThreadPoolExecutor(
            CORE_POOL_SIZE,
            MAX_POOL_SIZE,
            KEEP_ALIVE_TIME, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(QUEUE_CAPACITY),
            new ThreadPoolExecutor.AbortPolicy() // 큐가 가득 차면 예외 발생
        );
    }

    // Singleton 인스턴스를 가져오는 방법: enum을 통해 인스턴스를 직접 접근
    public static CustomThreadPool getInstance() {
        return INSTANCE;
    }

    // 작업 실행 메서드
    public void executeTask(Runnable task) {
        executor.execute(task);
        System.out.println("Active Threads: " + executor.getActiveCount() +
                           ", Queue Size: " + executor.getQueue().size());
    }

    // 안전한 종료
    public void shutdown() {
        executor.shutdown();
        try {
            if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
        }
    }
}

public class MainApp {
    public static void main(String[] args) {
        CustomThreadPool pool = CustomThreadPool.getInstance();

        for (int i = 1; i <= 10; i++) {
            final int taskNumber = i;
            pool.executeTask(() -> {
                System.out.println(Thread.currentThread().getName() + " - Task " + taskNumber);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        pool.shutdown(); // 모든 작업이 끝나면 스레드 풀 종료
    }
}

 

● 출력결과 : 

pool-1-thread-1 - Task 1
pool-1-thread-2 - Task 2
Active Threads: 2, Queue Size: 5
Active Threads: 2, Queue Size: 5
...
pool-1-thread-3 - Task 3
pool-1-thread-4 - Task 4
...
pool-1-thread-10 - Task 10

 

 

● 핵심정의 :  

  1. Singleton 패턴 적용
  2. ThreadPoolExecutor 활용 (최소/최대 스레드 개수, 대기 큐 설정)
  3. 안전한 종료 메커니즘 제공
  4. 다중 작업을 효율적으로 처리 가능

이러한 커스텀 스레드 풀은 대량의 작업을 병렬 처리하는 서버 애플리케이션, 백그라운드 작업, 데이터 처리 시스템 등에 활용될 수 있습니다.

 

 


 

● 단순히 고정된 스레드 풀을 사용하려는 목적 : 

 public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(2);

        for (int i = 1; i <= 5; i++) {
            final int taskNumber = i;
            pool.execute(() -> {
                System.out.println(Thread.currentThread().getName() + " - Task " + taskNumber);
                try {
                    Thread.sleep(1000); // 작업이 1초 걸린다고 가정
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

        pool.shutdown();
    }

 출력결과 : 

pool-1-thread-1 - Task 1
pool-1-thread-2 - Task 2
pool-1-thread-2 - Task 3
pool-1-thread-1 - Task 4
pool-1-thread-1 - Task 5

 

단순히 고정된 스레드 풀을 사용하려는 목적이라면 ExecutorService pool = Executors.newFixedThreadPool(5); 방식이 훨씬 간단하고 효율적입니다.

  1. 간결한 코드 : 스레드 풀을 사용하는 데 필요한 설정이 매우 직관적이고 간단합니다. 풀의 크기만 지정하면 되므로 복잡한 설정 없이 바로 사용할 수 있습니다.
  2. 자동 관리 : Executors.newFixedThreadPool()을 사용하면 스레드 풀의 크기와 관리를 자동으로 처리해줍니다. 즉, 풀에 들어갈 스레드를 자동으로 생성하고, 작업 큐를 관리하며, 스레드의 유휴 시간과 종료 등을 관리합니다.
  3. 효율성 : 기본적으로 필요한 스레드 수만큼만 스레드가 생성되므로 불필요한 스레드를 생성하지 않고, 시스템 자원을 효율적으로 사용할 수 있습니다.
  4. 스레드 관리에 대한 신경을 덜어줌 : ExecutorService는 스레드 풀의 관리 및 스케줄링을 자동으로 처리해주기 때문에 개발자가 스레드 관리에 대해 신경 쓸 필요가 없습니다. 단순히 작업을 제출하면 풀에서 처리해줍니다.

'Java' 카테고리의 다른 글

Java Singleton  (2) 2025.03.01
Java 스레드 실행 및 안전한 종료 방법  (2) 2025.02.16
JAVA Index 기반 문자열 변형 및 랜덤 셔플러  (2) 2025.02.15
비트 연산  (2) 2025.01.31
Java 빌더 패턴 (Builder Pattern)  (0) 2025.01.29

+ Recent posts