기본 패턴
java
ExecutorService pool = Executors.newFixedThreadPool(4);
Future<Integer> future = pool.submit(() -> 40 + 2);
int value = future.get();
pool.shutdown();설명
- 스레드를 직접 만들면 교육용 예제는 단순해도, 실제 서비스에서는 생성 비용과 종료 시점, 개수 제한을 직접 다뤄야 해서 금방 복잡해집니다.
ExecutorService는 이 문제를 thread pool이라는 형태로 관리합니다. Runnable은 반환값이 없고,Callable<V>는 결과를 반환하거나 checked exception을 던질 수 있습니다. 계산 작업이나 I/O 작업 결과를 받아야 할 때Callable이 자연스럽습니다.submit()은 작업을 큐에 넣고Future를 돌려줍니다.Future는 나중에 결과를 기다리거나 완료 여부를 확인하거나 취소할 수 있게 해 주는 핸들입니다.- 핵심은 "작업 정의", "실행 자원", "결과 회수"를 분리하는 데 있습니다. 이 분리가 되어야 요청량이 늘어날 때 thread 수를 통제하고, timeout이나 cancellation 정책도 붙일 수 있습니다.
- 실무에선
CompletableFuture나 framework 비동기 모델을 더 많이 보더라도, 그 아래 기초 감각은 대부분ExecutorService와Future위에서 이해하는 편이 좋습니다.
빠른 정리
| 요소 | 의미 |
|---|---|
ExecutorService | 작업 실행을 관리하는 thread pool |
Callable<V> | 결과를 돌려주는 작업 |
Future<V> | 결과, 취소, 완료 여부를 다루는 핸들 |
submit() | 작업 제출 |
shutdown() | 새 작업을 받지 않고 정리 시작 |
주의할 점
Future.get()은 결과가 준비될 때까지 현재 thread를 block합니다. 비동기 구조를 만든 뒤에도 무심코 get()을
남발하면 결국 동기 코드처럼 병목이 생길 수 있습니다.
참고 링크
3 sources