ExecutorService 소개
ExecutorService는 Java의 java.util.concurrent 패키지에 포함된 인터페이스로, 스레드 관리를 단순화하고 작업을 병렬로 실행할 수 있게 해줍니다.
- 전통적인 스레드 생성 방식은 코드가 복잡해지고, 스레드 수를 관리하기 어렵다는 단점이 있습니다.
ExecutorService는 이런 문제를 해결하기 위해 등장했습니다.
주요 기능 및 사용법
Thread Pool 관리:
ExecutorService는 미리 정의된 수의 스레드를 가진 스레드 풀을 관리합니다. 이를 통해 새로운 작업이 제출될 때마다 새로운 스레드를 생성하는 대신, 이미 존재하는 스레드를 재사용할 수 있어 효율적입니다.
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.submit(() -> {
});
작업 제출 및 실행:
- 작업은
Runnable 또는 Callable 인터페이스를 구현하여 submit 메서드를 통해 제출할 수 있습니다.
Future<String> future = executorService.submit(() -> {
return "작업 결과";
});
작업 완료 대기:
Future 객체를 사용하여 작업이 완료될 때까지 대기하거나, 작업 결과를 가져올 수 있습니다.
String result = future.get();
Graceful Shutdown:
shutdown 메서드를 호출하여 더 이상 새로운 작업을 받지 않고, 현재 진행 중인 작업이 완료되면 종료합니다.
shutdownNow 메서드를 호출하면 즉시 모든 작업을 중단합니다.
executorService.shutdown();
비슷한 도구와의 비교
Thread:
- 장점: 간단한 멀티스레딩 구현.
- 단점: 스레드 수 관리가 어렵고, 성능 저하 우려.
Thread thread = new Thread(() -> {
});
thread.start();
ForkJoinPool:
- 장점: 작업을 작은 단위로 나눠 병렬로 처리하는 데 최적화됨. 특히, 재귀적 작업에 유리.
- 단점: 사용이 복잡하고 모든 상황에서 효율적이지 않음.
ForkJoinPool forkJoinPool = new ForkJoinPool();
forkJoinPool.submit(() -> {
});
CompletableFuture:
- 장점: 비동기 프로그래밍을 지원하며, 콜백을 통해 작업 완료 시 후속 작업을 정의할 수 있음.
- 단점: 초기 학습 곡선이 있음.
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
return "작업 결과";
});
completableFuture.thenAccept(result -> {
});
ExecutorService의 장점
- 효율적인 스레드 관리: 스레드 풀을 통해 스레드 재사용 및 관리가 용이.
- 높은 유연성: 다양한 스레드 풀 유형 (
FixedThreadPool, CachedThreadPool, ScheduledThreadPool) 제공.
- 간단한 인터페이스: 작업 제출 및 관리가 용이.
ExecutorService의 단점
- 자원 관리 주의 필요: 스레드 풀 종료를 명시적으로 처리하지 않으면 메모리 누수 발생 가능.
- 복잡한 사용법: 간단한 작업에는 불필요하게 복잡할 수 있음.
- 디버깅 어려움: 비동기 작업에서 발생하는 예외나 오류를 추적하기 어려울 수 있음.