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 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- ExecutorService와 스레드 풀 유형
- 작업 제출: Runnable과 Callable
- Future와 오류 처리
- 반복 작업을 위한 ScheduledExecutorService