Executor
📌 Executor란?
info
Executor는 비동기 작업을 처리하기 위한 스레드 풀 관리 컴포넌트이다.
작업을 별도의 스레드에서 실행하여 병렬 처리 및 성능 개선을 가능하게 한다.
📌 왜 Executor를 사용하는가
- 비동기 처리 (@Async)
- 병렬 API 호출
- IO 작업 병목 해소
- 대량 요청 처리
단일 스레드 처리 구조를 병렬 구조로 개선하기 위해 사용한다.
📌 기본 구조
Executor는 다음 3가지 요소로 구성된다.
| 요소 | 설명 |
|---|---|
| Thread Pool | 스레드 집합 |
| Queue | 대기 작업 저장 |
| Worker Thread | 실제 작업 실행 |
📌 주요 설정 값
| 옵션 | 설명 |
|---|---|
| corePoolSize | 기본 유지 스레드 수 |
| maxPoolSize | 최대 스레드 수 |
| queueCapacity | 작업 대기 큐 크기 |
| keepAliveSeconds | 유휴 스레드 유지 시간 |
📌 기본 Executor 설정 (Spring Boot)
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("EXEC-");
executor.initialize();
return executor;
}
📌 CompletableFuture.supplyAsync
info
supplyAsync는 비동기 작업을 실행하고 결과를 반환하는 메서드이다.
CompletableFuture<String> future =
CompletableFuture.supplyAsync(() -> "result", taskExecutor);
📌 병렬 처리 + 결과 취합
List<CompletableFuture<String>> futures = list.stream()
.map(item -> CompletableFuture.supplyAsync(() -> process(item), taskExecutor))
.toList();
// 모든 작업 완료 대기
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
// 결과 취합
List<String> results = futures.stream()
.map(CompletableFuture::join)
.toList();
allOf→ 전체 완료 대기join()→ 결과 수집
병렬 처리 후 결과를 하나의 리스트로 취합 가능
📌 Semaphore란?
info
Semaphore는 동시에 실행 가능한 작업 수를 제한하는 동시성 제어 도구이다.
📌 Semaphore + Executor
Semaphore semaphore = new Semaphore(10);
CompletableFuture<String> future =
CompletableFuture.supplyAsync(() -> {
try {
semaphore.acquire();
return callExternalApi();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
} finally {
semaphore.release();
}
}, taskExecutor);
📌 전체 흐름 정리
Semaphore semaphore = new Semaphore(10);
List<CompletableFuture<String>> futures = list.stream()
.map(item -> CompletableFuture.supplyAsync(() -> {
try {
semaphore.acquire();
return process(item);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
} finally {
semaphore.release();
}
}, taskExecutor))
.toList();
// 전체 완료 대기
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
// 결과 취합
List<String> results = futures.stream()
.map(CompletableFuture::join)
.toList();
📌 핵심 정리
| 요소 | 역할 |
|---|---|
| Executor | 스레드 풀 관리 |
| supplyAsync | 비동기 실행 |
| Semaphore | 동시 실행 제한 |
| allOf | 전체 완료 대기 |
| join | 결과 취합 |
📌 Executor 분리 전략
tip
작업 성격에 따라 Executor를 분리하는 것이 중요하다. API Response로 FileStream을 사용한다면 분리하는 것을 권장한다.
IO 작업용 Executor
executor.setCorePoolSize(20);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(200);
CPU 작업용 Executor
executor.setCorePoolSize(4);
executor.setMaxPoolSize(8);
executor.setQueueCapacity(50);
📌 결론
Executor + CompletableFuture + Semaphore를 조합하면
- 비동기 처리
- 병렬 처리
- 동시성 제어
- 결과 취합
을 모두 구현할 수 있다.
댓글남기기