0Pricing
Java Academy · 강의

ExecutorService와 스레드 풀 유형

고정형, 캐시형, 단일, 예약 스레드 풀을 만들고 각각을 사용할 시점을 이해합니다.

ExecutorService와 스레드 풀 유형은(는) CoddyKit의 무료 Java Academy 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Java Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

스레드 풀을 사용하는 이유

모든 작업마다 새 스레드를 만드는 것은 비용이 많이 듭니다. 스레드를 생성하고 제거하는 데 오버헤드가 발생하기 때문입니다. 스레드 풀은 고정된 스레드 집합을 재사용하여 많은 작업을 실행하므로 오버헤드를 줄이고 리소스 소비를 제어합니다.

import java.util.concurrent.*;

// Bad practice: new thread per task
new Thread(() -> System.out.println("task")).start();

// Better: reuse threads from a pool
ExecutorService pool = Executors.newFixedThreadPool(4);
pool.submit(() -> System.out.println("task from pool"));
pool.shutdown();

newFixedThreadPool

정확히 N개의 스레드를 가진 풀을 생성합니다. 추가 작업은 스레드를 사용할 수 있게 될 때까지 대기열에 들어갑니다. 원하는 병렬 처리 수준을 알고 있을 때 적합합니다.

ExecutorService pool = Executors.newFixedThreadPool(4);

for (int i = 0; i < 10; i++) {
    final int taskId = i;
    pool.submit(() -> {
        System.out.println("Task " + taskId + " on " + Thread.currentThread().getName());
    });
}

pool.shutdown();
pool.awaitTermination(10, TimeUnit.SECONDS);

newCachedThreadPool

필요할 때 스레드를 생성하고 유휴 스레드를 재사용합니다. 60초 동안 유휴 상태인 스레드는 종료됩니다. 수명이 짧은 작업이 많을 때는 적합하지만, 장시간 실행되거나 무제한으로 늘어날 수 있는 작업에서는 메모리가 고갈될 수 있어 주의해야 합니다.

ExecutorService cached = Executors.newCachedThreadPool();

for (int i = 0; i < 100; i++) {
    cached.submit(() -> {
        // quick task
        double result = Math.sqrt(Math.random());
    });
}
cached.shutdown();

newSingleThreadExecutor

정확히 하나의 스레드를 가진 풀입니다. 작업은 제출된 순서대로 순차 실행됩니다. 공유 리소스에 대한 접근을 직렬화할 때 적합합니다.

ExecutorService single = Executors.newSingleThreadExecutor();

single.submit(() -> System.out.println("Task 1"));
single.submit(() -> System.out.println("Task 2"));
single.submit(() -> System.out.println("Task 3"));
// Always prints: Task 1, Task 2, Task 3 (sequential)
single.shutdown();

newScheduledThreadPool

예약 작업과 반복 작업을 지원합니다. 폴링, 재시도, 백그라운드 유지 관리에 사용합니다.

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);

// One-shot delay
scheduler.schedule(() -> System.out.println("Delayed!"),
    3, TimeUnit.SECONDS);

// Fixed rate (every 2 seconds)
scheduler.scheduleAtFixedRate(
    () -> System.out.println("Tick"),
    0, 2, TimeUnit.SECONDS);

// Run later: scheduler.shutdown();

VirtualThreadExecutor (Java 21 이상)

Java 21에서는 가볍고 생성 비용이 저렴한 가상 스레드를 도입합니다. 입출력 중심 작업에는 Executors.newVirtualThreadPerTaskExecutor()를 사용합니다.

// Java 21+
try (var exec = Executors.newVirtualThreadPerTaskExecutor()) {
    for (int i = 0; i < 10_000; i++) {
        final int id = i;
        exec.submit(() -> {
            Thread.sleep(100); // blocks without tying up OS thread
            System.out.println("vthread " + id);
        });
    }
} // auto-shutdown

적절한 풀 선택

지침은 다음과 같습니다.

  • CPU 집중 작업: N을 CPU 코어 수로 설정한 고정 풀
  • 입출력 중심 작업: 캐시된 풀 또는 가상 스레드
  • 순차 작업: 단일 스레드 실행기
  • 예약/반복 작업: 예약 스레드 풀
int cores = Runtime.getRuntime().availableProcessors();
ExecutorService cpuPool = Executors.newFixedThreadPool(cores);
System.out.println("Pool size: " + cores);

사용자 지정 ThreadPoolExecutor

세밀하게 제어하려면 ThreadPoolExecutor를 직접 생성합니다.

ThreadPoolExecutor tpe = new ThreadPoolExecutor(
    2,           // corePoolSize
    10,          // maximumPoolSize
    60L,         // keepAliveTime
    TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(100), // work queue
    Executors.defaultThreadFactory(),
    new ThreadPoolExecutor.CallerRunsPolicy() // rejection policy
);
tpe.submit(() -> System.out.println("Custom pool task"));
tpe.shutdown();

안전한 종료

스레드를 해제하려면 항상 ExecutorService를 종료해야 합니다. shutdown()은 새 작업을 받지 않지만 대기 중인 작업은 완료합니다. shutdownNow()는 실행 중인 작업을 취소하려고 시도합니다.

ExecutorService pool = Executors.newFixedThreadPool(4);
// ... submit tasks ...

pool.shutdown(); // no new tasks
try {
    if (!pool.awaitTermination(30, TimeUnit.SECONDS)) {
        pool.shutdownNow(); // force cancel
    }
} catch (InterruptedException e) {
    pool.shutdownNow();
    Thread.currentThread().interrupt();
}

AutoCloseable로서의 ExecutorService (Java 19 이상)

Java 19 이상에서 ExecutorService는 AutoCloseable을 구현합니다. 자동으로 종료되도록 try-with-resources를 사용합니다.

// Java 19+ (preview), Java 21 stable
try (ExecutorService pool = Executors.newFixedThreadPool(4)) {
    pool.submit(() -> System.out.println("task"));
} // pool.close() called: waits for tasks then shuts down

거부 정책

풀이 포화 상태가 되면(모든 스레드가 사용 중이고 대기열도 가득 찬 경우) 거부 정책이 초과 작업을 처리합니다.

  • AbortPolicy(기본값): RejectedExecutionException을 발생시킵니다.
  • CallerRunsPolicy: 호출한 스레드가 작업을 실행합니다.
  • DiscardPolicy: 작업을 조용히 버립니다.
  • DiscardOldestPolicy: 대기열에서 가장 오래된 작업을 버립니다.

확인 문제

무거운 계산을 수행하는 CPU 집중 작업이 있습니다. 고정 스레드 풀에는 몇 개의 스레드가 있어야 합니까?

복습: ExecutorService와 스레드 풀

핵심 내용은 다음과 같습니다.

  • 고정 풀: N개의 고정 스레드로 구성되며 추가 작업은 대기열에 들어갑니다.
  • 캐시된 풀: 필요에 따라 확장되며 유휴 스레드는 60초 후 만료됩니다.
  • 단일 스레드: 순차 실행 순서가 보장됩니다.
  • 예약 풀: 지연 작업과 반복 작업에 사용합니다.
  • 항상 shutdown()을 호출하고, 정상적으로 종료하려면 awaitTermination()을 사용합니다.

자주 묻는 질문

“ExecutorService와 스레드 풀 유형” 강의는 무료인가요?

네 — “ExecutorService와 스레드 풀 유형” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Java Academy 강의 전체를 잠금 해제할 수 있습니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“ExecutorService와 스레드 풀 유형”에서 뭘 배우나요?

고정형, 캐시형, 단일, 예약 스레드 풀을 만들고 각각을 사용할 시점을 이해합니다. 브라우저에서 직접 실행하는 실습 코드로 Java Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Java Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Java Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.

“ExecutorService와 스레드 풀 유형” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Java Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Java Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. ExecutorService와 스레드 풀 유형
  2. 작업 제출: Runnable과 Callable
  3. Future와 오류 처리
  4. 반복 작업을 위한 ScheduledExecutorService
← Java Academy(으)로 돌아가기