ThreadPool
- 다수의 쓰레드를 미리 생성하고 관리하여 작업을 효율적으로 처리하는 디자인 패턴
- 자바에서는 쓰레드 풀을 사용할 수 있도록 Executor 프레임워크 제공
1. ThreadPool이 필요한 이유
- 쓰레드 생성 비용 절감
- 쓰레드 재사용
- 동시성 제어
- 대량 요청으로부터 시스템 보호
1.1 쓰레드 생성 비용 절감
- 쓰레드 생성은 비싼 작업
- 따라서 쓰레드 풀은 쓰레드를 미리 생성하고 초기화하여 대기 상태로 유지함으로써 쓰레드 생성 비용을 절감시킴
1.2 쓰레드 재사용
- 쓰레드 풀은 작업이 종료된 쓰레드를 대기 상태로 전환시킨 뒤 재사용하며 반복적인 쓰레드 생성 및 삭제에 따른 오버헤드를 피하고 성능 향상 도모
1.3 동시성 제어
- 쓰레드 풀은 동시에 실행되는 쓰레드의 개수를 제어함에 따라 시스템 리소스의 과도한 사용을 방지
- 쓰레드 간 경합으로 인한 성능 저하 또한 예방
1.4 대량 요청으로부터 시스템 보호
- 클라이언트의 동시 접속이 증가하더라도 리소스가 허용하는 한도 내에서 요청을 대기시켜 시스템 오류 방지
2. ThreadPool 구현 시 고려해야 하는 요소
- 쓰레드 생성 및 관리 메커니즘
- 작업 큐
- 동기화 메커니즘
- 쓰레드 풀 설정
- 쓰레드 종료 및 리소스 관리 메커니즘
- 오류 처리 메커니즘
2.1 쓰레드 생성 및 관리 메커니즘
- 쓰레드 풀을 구현하기 위해 쓰레드 생성, 초기화, 재사용 및 제거 등을 효율적으로 처리할 수 있어야 함
2.2 작업 큐
- 작업들을 관리하는 대기열인 작업 큐 필요
- 작업 큐는 작업들을 저장하고 쓰레드가 작업을 가져와 실행할 수 있는 구조 제공
2.3 동기화 메커니즘
- 쓰레드 간의 경합 상황을 관리하고 동기화를 처리하여 데이터의 일관성 및 정확성 보장할 수 있어야 함
2.4 쓰레드 풀 설정
- 동시에 실행되는 쓰레드의 최대 갯수, 작업 큐의 크기, 그리고 작업 우선순위 등을 조절하는 매개변수
2.5 쓰레드 종료 및 리소스 관리 메커니즘
- 쓰레드 풀에 속한 쓰레드가 종료되거나 더 이상 필요하지 않을 경우 처리하고 관리할 수 있어야 함
2.6 오류 처리 메커니즘
- 작업 실행 중에 오류가 발생했을 때 적절한 조치를 취할 수 있어야 함
* 주의: 가용 쓰레드가 전부 작업 중인 상태에서 작업 큐도 꽉 찰 경우 오류가 발생해 비정상적 종료가 발생할 수 있음
Executor
- Exeuctor 프레임워크는 java.util.concurrent 패키지에 포함된 쓰레드 관리와 병렬 처리를 위한 고급 기능들을 제공하는 포괄적인 라이브러리
- 복잡한 쓰레드 생성, 관리, 그리고 동기화 등의 작업을 단순화
- 성능 향상하기 위한 다양한 클래스 및 인터페이스 제공
1. Executor 프레임워크 구조
1.1 Executor
- Executor 프레임워크의 핵심 인터페이스
- 단일 메서드 execute(Runnable commanad)를 정의
- 작업 제출 시 Executor 구현체가 적절한 쓰레드를 생성하고 작업을 실행
- 각 작업의 제출과 작업의 실행(실행 방법, 쓰레드 사용, 스케줄링 등의 세부사항)을 분리하는 방법 제공
- execute 메서드에 Runnable 객체를 넘기는 것을 작업 제출
- Runnable 명령은 Executor 구현 방식에 따라 새 쓰레드, 풀 쓰레드 또는 호출 쓰레드에서 실행 가능하며 이를 작업 실행으로 구분
- ex) Runnable의 run() 메서드를 통해 실행하면 main 쓰레드에서 직접 실행
- ex) thread.start() 메서드를 통해 실행하면 쓰레드를 생성해서 비동기 실행
- ex) threadPool.submit(runnable) 메서드를 통해 실행하면 쓰레드 풀에 등록해서 비동기 실행
- 정리하자면 직접 쓰레드를 실행하고 작업을 실행하는 것이 아니라 작업 제출 시 쓰레드 생성과 작업 실행을 Executor에서 처리하도록 위임하는 것이 더 유연하고 좋은 설계이기 때문에 Executor는 내부적으로 작업의 제출과 실행을 분리
1.2 Executor 구현체
- 쓰레드 풀의 생성, 관리, 작업 큐, 그리고 쓰레드 생성 및 삭제 정책 등을 다양한 설정으로 제어 가능
- ex) ThreadPoolExecutor, ScheduledThreadPoolExecutor
1.3 ExecutorService
- Executor의 한계를 보완해주는 확장 버전
- 종료를 관리하는 메서드와 하나 이상의 비동기 작업 진행 상황을 추적하는 데 사용할 수 있는 Future를 생성할 수 있는 메서드를 제공하는 Executor
- Executor에서 제공하지 못하는 작업의 제출(submit)과 쓰레드 풀의 종료를 관리하기 위한 메서드들을 추가 제공
- 작업 제출과 실행에 더해 작업의 상태 추적 작업 결과 반환, 작업 취소 등 다양한 작업 관리 기능을 제공
- ExecutorService는 작업 제출부터 작업 실행, 작업 완료, 쓰레드 풀 종료, 자원 회수까지의 과정을 포함하는 라이프 사이클을 가지고 있음
1.4 ScheduledExecutorService
- ExecutorService의 확장 버전
- 특정 시간 또는 주기적으로 작업을 실행할 수 있도록 스케줄링 메서드 제공
Runnable & Callable
- Runnable과 Callable은 모두 별도의 쓰레드에서 실행할 수 있는 작업을 나타내는 데 사용되는 인터페이스
1. Runnable vs Callable
기능 | Runnable | Callable |
메서드 시그니처 | run() 메서드를 정의하며 인수 없음 | call() 메서드를 정의하며 인수가 없고 결과와 예외 구문 있음 |
예외 처리 | Checked Exception 예외를 던질 수 없음 | Chekced Exception 예외를 던질 수 있음 |
용도 | 쓰레드에서 실행할 작업 정의 | 결과를 반환하며 예외를 처리해야 하는 작업 정의 |
결과 반환 | 작업이 완료되더라도 결과 반환 X | 작업 완료 시 결과를 반환하며 Future로 결과 추적 가능 |
2. Callable & Future
2.1 전체적인 flow
- 메인 쓰레드가 ExecutorService를 통해 Callable을 제출
- ExecutorService 내부적으로 Callable을 실행하여 결과를 FutureTask에 저장
- 해당 결과는 Future를 통해 메인 쓰레드로 반환
2.2 FutureTask의 run() 메서드
Future & Callback
- Future와 Callback은 비동기 프로그래밍에서 사용되는 패턴
- 비동기 작업의 결과를 처리하거나 작업이 완료되었을 때 수행할 동작을 정의하며 사용
- 자바에서는 Future 인터페이스와 구현체들을 제공하고 있으며 다양한 Callback 패턴 활용
- Future는 비동기 작업에서 쓰레드 간 결과를 받을 때 유용
- 비동기 작업 쓰레드 간 실행의 흐름이 독립적이기 때문에 비동기 작업의 완료 시점에 결과를 얻을 수 있어야 하는데 이때 Callback이 유용
1. Future vs Callback
구분 | Future | Callback |
정의 | 비동기 작업의 결과를 나타내는 객체 | 비동기 작업이 완료되었을 때 수행할 동작을 정의한 인터페이스 혹은 클래스 |
블로킹 여부 | 비동기 작업이 완료될 때까지 블로킹 (아직 결과값이 구해지지 않은 상태에서 get() 호출 시 blocking) |
블로킹되지 않고 비동기 작업이 완료되면 콜백 호출 |
작업 결과 | 비동기 작업이 완료되면 결과를 얻을 수 있음 | 콜백 메서드를 통해 작업 결과를 처리 |
활용 용도 | 결과를 받아오는 작업에서 활용 | 비동기 작업의 완료 후 동작을 정의할 때 주로 활용 |
2. Future를 활용한 비동기 작업
특징
- Future 객체는 바로 생성되는 반면 결괏값은 작업이 완료되어야 세팅됨
- 따라서 future.get() 메서드를 통해 결과를 반환받을 때 blocking 구간이 존재
3. Callback을 활용한 비동기 작업
특징
- Future와 달리 blocking 구간 존재 X
- Callback의 경우 결괏값이 다 반환되어야 Callback 인스턴스 생성
4. Future와 Callback을 혼합한 비동기 작업
특징
- Main Thread -> Callback -> Future 순으로 호출하는 작업
- Callback에서 Future를 호출하므로 Callback에서 결괏값을 반환받는 과정에서 blocking 구간이 존재하지만 Main Thread 입장에서는 blocking 구간 존재 X
Future 구조와 API
- Future는 비동기 작업의 결과를 나중에 가져올 수 있도록 도와주는 인터페이스
- Future는 비동기 작업이 완료되었는지 여부 확인 가능하며 조건에 따라 작업을 취소할 수도 있고 작업의 결과를 얻는 방법을 제공
- 기존의 Future는 작업의 결과를 가져올 때까지 blocking 되며 여러 작업을 조합하는 문제, 예외 처리의 어려움 등이 존재
- 이러한 단점을 보완하기 위해 자바 8+ 버전부터는 CompletableFuture와 같은 개선된 비동기 도구들이 제공
1. Future API
1.1 boolean cancel(boolean mayInterruptIfRunning)
- 작업이 시작되지 않은 경우 해당 작업은 실행되지 않으며 true를 반환
- 작업이 이미 완료되었거나 취소되었거나 다른 이유로 취소할 수 없는 경우 아무런 일도 발생하지 않으며 false 반환
- 작업이 이미 시작된 경우 mayInterruptIfRunning 매개변수에 따라 결정
- true일 경우 해당 작업을 중지시키기 위해 현재 작업을 실행 중인 쓰레드 interrupt 하며 작업 결과를 가져올 때 취소 예외 발생
- false일 경우 진행 중인 작업을 완료할 수도 있지만 작업 결과를 가져올 때 취소 예외 발생
- 정리하자면 결괏값을 가져오지 못할 때 취소 성공이라고 간주해도 됨
- 메서드의 반환 값은 작업이 현재 취소되었는지 여부를 반드시 나타내지 않기 때문에 정확한 결과는 isCancelled 메서드를 사용하는 것을 권장
- 작업과 연관된 모든 대기 중인 쓰레드를 깨우고 시그널을 보내며 done()을 호출하고 callable을 null로 설정
1.2 boolean isCancelled()
- 해당 작업이 정상적으로 완료되기 전에 취소되었으면 true 반환
- 취소 여부를 명확하게 확인할 때 사용
1.3 boolean isDone()
- 작업이 완료될 경우 true를 반환
- 정상 종료, 예외 또는 취소를 포함한 모든 경우에 true를 반환
- 완료라는 개념 내 정상 종료, 예외, 취소가 들어감
- 정상종료일 경우 FutureTask의 set 메서드에서 이루어지고
- 예외 발생 시 FutureTask의 setException 메서드에서 이루어지며
- 취소 시 FutureTask의 cancel() 메서드에서 이루어짐
1.4 V get() throws InterruptedException, ExecutionException
- 작업 결과를 반환하며 작업이 완료될 때까지 쓰레드 대기 (blocking 구간 존재)
- 작업이 예외를 던져 실패하면 해당 예외가 Future.get()을 호출할 때 발생
- 실패했을 경우 실패한 정보나 결괏값을 Future에서 얻어올 수 없고 예외 구문에서 확인해야 함
- CancellationException: 작업이 취소된 경우
- ExecutionException: 작업이 예외를 발생시킨 경우
- InterruptedException: 현재 쓰레드가 대기 중에 interrupt 된 경우
1.5 V get(long timeout, TimeUnit unit)
- 지정한 시간 동안 대기하고 해당 시간 내 완료되면 작업 결과를 반환
- 시간 내 완료되지 않으면 TimeoutException 예외 발생
2. FutureTask의 상태 변수
2.1 작업에 따른 상태 변화
- 정상 완료: NEW -> COMPLETING -> NORMAL
- 예외 발생: NEW -> COMPLETING -> EXCEPTIONAL
- 작업 취소 인자가 true인 경우: NEW -> INTERRUPTING -> INTERRUPTED
- 작업 취소 인자가 false인 경우: NEW -> CANCELLED
2.2 정상 완료 (set)
2.3 예외 발생 (setException)
2.4 작업 취소 (cancel)
3. Future 예제
ExecutorService
- 비동기 작업을 실행하고 관리하기 위한 두 가지 메서드 제공
- void execute(Runnable runnable): 작업을 제출하면 작업을 실행하고 종료
- Future submit(Callable callable): 작업을 제출하면 작업을 실행과 동시에 결괏값을 포함한 Future를 반환
- 앞서 Future와 Callback을 혼합한 비동기 작업 참고
1. execute() vs submit()
기능 | execute() | submit() |
작업 유형 | Runnable 작업을 ThreadPool에서 실행 | Runnable 또는 Callable 작업을 ThreadPool에서 실행 |
작업 완료 대기 | 작업을 수행하고 완료되기를 기다리지 않음 | 작업이 완료될 때까지 결과를 기다릴 수 있음 |
결과 반환 | 결과를 반환하지 않음 | 작업의 결과를 Future로 반환하여 추후에 결과를 가져올 수 있음 |
반환 결과 | void | Future 객체 |
1.1 Future<T> submit(Callable<T> task)
- Callable 작업을 실행하는 메서드
- 작업의 결과를 나타내는 Future를 반환하며 Future에는 작업의 결과가 저장되어 있음
1.2 Future<T> submit(Runnable task, T result)
- Runnable 작업을 실행하는 메서드
- 작업의 결과를 나타내는 Future를 반환하며 Future에는 작업의 결과가 저장되어 있음
1.3 Future<?> submit(Runnable task)
- Runnable 작업을 실행하는 메서드
- 작업의 결과를 나타내는 Future를 반환하며 Future에는 아무런 결과가 존재하지 않음
- execute()와 차이가 없는 메서드
2. ThreadPool 중단 및 종료
- ExecutorService는 쓰레드 풀 종료를 위해 두 가지 메서드 제공
- void shutdown()
- List<Runnable> shutdownNow()
2.1 void shutdown()
- 정상적인 쓰레드 풀 종료를 위한 메서드
- 실행 중인 쓰레드를 강제로 interrupt 하지 않기 때문에 인터럽트에 응답하는 작업이나 InterruptedException 예외 구문을 작성할 필요 없음
- 이전에 제출된 작업은 실행하고 더 이상 새로운 작업은 수락하지 않음
- 작업이 모두 완료될 경우 ExecutorService 종료
- graceful shutdown
2.2 List<Runnable> shutdownNow()
- 이전에 제출된 작업을 취소하고 현재 실행 중인 작업도 중단시키려고 시도한 뒤 작업 대기 중이었던 작업 목록을 반환
- 실행 중인 쓰레드를 강제로 인터럽트 하지만 해당 작업이 인터럽트에 응답하는 작업이 아닌 경우 작업 종료를 보장하지 않음
- 작업 종료를 위해 Thread.isInterrupted() 혹은 sleep()과 같은 인터럽트 API 사용해야 함
- interrupt에 InterruptedException이 발생하는 코드 구문이 있어야지만 즉시 종료
2.3 shutdown 이후
- shutdown 후 작업 제출 시도 시 RejectedExecutionException 예외 발생
- shutdown 호출한 쓰레드는 실행 중인 작업이 종료될 때까지 기다리지 않고 바로 다음 라인 실행
- 만약 쓰레드가 메서드 호출 후 blocking 되길 원한다면 awaitTermination()를 사용해야 함
3. ExecutorService 작업 종료 대기 및 확인
- ExecutorService는 작업 종료 대기 및 확인을 위한 메서드 제공
3.1 boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException
- 종료 요청 후 모든 작업이 실행 완료될 때까지 또는 타임아웃이 발생하거나 현재 쓰레드가 interrupt될 때까지 블로킹
- 종료가 완료되면 true를 반환
- 종료가 타임아웃 발생 전에 완료되지 않으면 false를 반환
- shutdown 호출한 시점 이후 대기하고 싶을 경우 awaitTermination 호출
- timeout 이후에도 살아 있으면 shutdownNow() 호출
3.2 boolean isShutdown()
- ExecutorService의 shutdown() 혹은 shutdownNow() 메서드가 호출 후 종료 절차가 시작되었는지를 나타냄
- 종료 절차 중이거나 완전히 종료된 상태일 때 true 반환
3.3 boolean isTerminated()
- ExecutorService가 완전히 종료되어 더 이상 어떠한 작업도 수행하지 않는 상태인지를 나타냄
- 모든 작업과 쓰레드가 완전히 종료된 후에 true를 반환
- shutdown 또는 shutdownNow가 먼저 호출되지 않은 경우에는 isTerminated가 절대 true가 되지 않음
4. ExecutorService 다중 작업 처리
- ExecutorService는 여러 작업을 실행하고 결과를 수집하는 데 사용되는 메서드를 제공
- List<Future> invokeAll(Collection tasks) throws InterruptedException
- List<Future> invokeAll(Collection tasks, long timeout, TimeUnit unit) throws InterruptedException
- T invokeAny(Collection tasks) throws InterruptedException, ExecutionException
- T invokeAny(Collection tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
4.1 List<Future> invokeAll(Collection tasks) throws InterruptedException
- 여러 개의 Callable 작업을 동시에 실행
- 모든 작업이 완료될 때까지 블록 되며 각 작업의 결과를 나타내는 Future 객체의 리스트 반환
- 작업 완료 시 Future.isDone()이 true가 되며 작업은 정상적으로 종료되거나 예외를 던져 종료될 수 있음
- 대기 중에 interrupt가 발생한 경우 아직 완료되지 않은 작업들은 취소됨
4.2 List<Future> invokeAll(Collection tasks, long timeout, TimeUnit unit) throws InterruptedException
- 4.1과 기능은 동일하고 시간 경과와 관련된 부분문 차이 남
- 타임아웃이 발생하거나 모든 작업이 완료될 때까지 블록 되며 각 작업의 결과를 나타내는 Future 객체의 리스트를 반환
- 타임아웃이 발생한 경우 작업 중 일부는 완료되지 않을 수 있으며 완료되지 않은 작업은 취소됨
* 4.1과 4.2의 invokeAll() 메서드 실행 시간은 작업들 중 가장 오래 걸리는 작업만큼 시간이 소요됨
4.3 T invokeAny(Collection tasks) throws InterruptedException, ExecutionException
- 여러 개의 Callable 작업을 동시에 실행하고 그중 가장 빨리 성공적으로 완료된 작업의 결과를 반환
- 어떤 작업이라도 성공적으로 완료하면 block을 해제하고 해당 작업의 결과 반환
- 정상적인 반환 또는 예외 발생 시 완료되지 않은 작업들은 모두 취소
4.4 T invokeAny(Collection tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
- 4.3 메서드와 기능은 동일하고 시간 경과와 관련된 부분만 차이 남
- 주어진 시간이 경과하기 전에 예외 없이 성공적으로 완료된 작업의 결과를 반환
* 4.3과 4.4의 invokeAny()는 작업들 중 가장 짧게 걸리는 작업만큼 시간이 소요됨
코드 부연 설명
- task들을 다 실행은 했기 때문에 interruptThread에 의해 Task 1과 Task 3가 interrupt 신호를 받고 InterruptedException 발생
- Task 2가 가장 빨리 작업을 완료하고 결과를 반환했으므로 결괏값은 Task 2
ScheduledExecutorService
- 주어진 지연 시간 후에 명령을 실행하거나 주기적으로 실행할 수 있는 ExecutorService를 상속한 인터페이스
- 작업의 예약 및 실행을 위한 강력한 기능 제공
- 시간 기반 작업을 조절하거나 주기적인 작업을 수행하는데 유용
1. ScheduledExecutorService API
- ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)
- ScheduledFuture<?> schedule(Callable callable, long delay, TimeUnit unit)
- ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
- ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
1.1 ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)
- 주어진 지연 시간 이후 Runnable 작업을 예약하고 ScheduledFuture 반환
- 예약된 작업은 한 번만 실행
- command: 실행할 작업
- delay: 실행을 지연할 시간
- unit: 지연 매개변수의 시간 단위
- ex) ScheduledFuture<?> future = ScheduledExecutorService.schedule(task, 5, TimeUnit.SECONDS);
- 5초 후에 태스크 실행하고 Future 반환
1.2 ScheduledFuture<?> schedule(Callable callable, long delay, TimeUnit unit)
- 주어진 지연 시간 이후 Callable 작업을 예약하고 ScheduledFuture 반환
- 예약된 작업은 한 번만 실행
- callable: 실행할 작업
- delay: 실행을 지연할 시간
- unit: 지연 배매개변수의 시간 단위
- ex) ScheduledFuture<?> future = ScheduledExecutorService.schedule(callable, 5, TimeUnit.SECONDS);
- 5초 후에 함수 실행하고 Future 반환
1.3 ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
- 초기 지연 시간 이후 Runnable 작업을 주기적으로 실해하도록 예약하고 ScheduledFuture 반환
- 이후에 주어진 주기로 실행
- 시작 시간을 기준으로 일정한 간격으로 작업을 반복
- command: 실행할 작업
- initDelay: 첫 번째 실행을 지연할 시간
- period: 연속적인 실행 사이의 주기
- unit: initDelay와 period 매개변수의 시간 단위
- 작업 시간이 period보다 클 경우 작업시간이 끝나고 실행되기 때문에 period가 의미 없을 수 있음
- 따라서 가급적 period >= 작업 시간이 되도록 설정하는 것을 추천
- ex) 작업 시간 2초, period 1초일 경우 작업이 끝나지 않아도 1초 만에 다시 실행될 것을 예상하지만 작업시간이 끝나고 즉 2초 후 실행되는 것을 확인할 수 있음
1.4 ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
- 초기 지연 시간 이후 Runnable 작업을 주기적으로 실해하도록 예약하고 ScheduledFuture 반환
- 작업이 완료되고 나서 지연 시간 후 실행
- 종료 시간을 기준으로 일정한 간격으로 작업을 반복
- command: 실행할 작업
- initDelay: 첫 번째 실행을 지연할 시간
- delay: 연속적인 실행 사이의 지연 시간
- unit: initDelay와 delay 매개변수의 시간 단위
2. ScheduledFuture
- ScheduledExecutorService를 사용하여 작업을 예약한 결과
- 주요 목적은 지연이나 주기적인 작업 실행을 위한 것
- 결과를 처리하는 것이 주목적은 아님
- future.get()을 통해 결과 확인 가능하나 주로 예약된 future 취소할 때 유용하게 쓰임
- getDelay(TimeUnit unit)을 통해 작업이 실행되기까지 남은 지연 시간 확인 가능
Executors
- Executors는 Executor, ExecutorService, ScheduledExecutorService, ThreadFactory 및 Callable 클래스를 위한 유틸리티 팩토리 클래스
- 쓰레드 풀 및 작업 스케줄링에 대한 다양한 메서드와 팩토리 메서드를 제공
- 복잡한 멀티 쓰레드 환경에서의 작업을 간단하게 다룰 수 있음
1.1 쓰레드 풀 생성 메서드
- ExecutorService 인터페이스를 구현한 클래스를 생성하고 반환하는 메서드
- ScheduledExecutorService 인터페이스를 구현한 클래스를 생성하고 반환하는 메서드
- ThreadFactory를 생성하고 반환하는 메서드
1. 고정 크기 쓰레드 풀 생성
1.1 고정 크기 쓰레드 풀 생성 메서드
- static ExecutorService newFixedThreadPool(int nThreads)
- static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory)
1.1.1 static ExecutorService newFixedThreadPool(int nThreads)
- 메서드의 매개변수로 원하는 쓰레드 개수를 지정할 수 있으며 지정한 개수만큼 쓰레드가 생성되어 작업을 처리
- 쓰레드 풀은 모든 쓰레드가 공유하는 대기열을 지니고 있으며 대기열은 무한한 크기의 대기열로 쓰레드가 가용 상태이면 대기 중인 작업을 처리
- 모든 쓰레드가 활성 상태인 상태에서 작업이 추가되면 쓰레드가 사용 가능한 상태가 될 때까지 작업들은 대기열에서 대기
- 쓰레드 풀이 종료하기 전에 어떤 쓰레드가 실패로 종료하게 되면 필요한 경우 새로운 쓰레드가 대신함
- nThreads가 0 미만일 경우 IllegalArgumentException 발생
1.1.2 static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory)
- 1.2.1과 기능은 동일
- 매개변수 ThreadFactory를 통해 쓰레드 생성과 관련된 로직 설정 가능
- 커스텀하게 쓰레드 생성 방식 적용, 쓰레드 이름, 우선순위 등을 설정 가능
1.1.2.1 ThreadFactory
- 쓰레드 생성과 관련된 세부 사항을 추상화하고 원하는 방식으로 쓰레드를 커스터마이징 할 수 있도록 지원하는 객체
- ThreadFactory를 사용하면 스레드를 직접 생성할 필요 없이 스레드 하위 클래스나 우선순위 등을 설정할 수 있음
- Executors.defaultThreadFactory 메서드는 기본적인 간단한 구현을 제공
2. 유동 및 단일 크기 쓰레드 풀 생성
2.1 유동 크기 쓰레드 풀 생성 메서드
- static ExecutorService newCachedThreadPool()
- static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)
2.1.1 static ExecutorService newCachedThreadPool()
- 작업이 제출되면 현재 사용 가능한 쓰레드가 있는지 확인하고 없으면 새 쓰레드를 생성하여 작업을 수행
- 일반적으로 많은 수의 짧은 작업들을 병렬로 실행하면서 처리 성능을 향상할 수 있음
- 60초 동안 사용되지 않은 쓰레드는 자동 종료되고 캐시에서 제거
- 쓰레드의 개수를 제한하지 않으며 작업 요청이 많을 때는 쓰레드 수가 증가하고 작업 요청이 감소하면 유휴 상태의 쓰레드가 종료되어 쓰레드 풀의 쓰레드 개수 조절 (auto-scaling)
- 작업을 담아놓고 대기시키는 blockingQueue가 아닌 쓰레드 간 작업을 주고 받는 동기 큐를 사용
- 작업이 제출되면 해당 작업 즉시 실행
2.1.2 static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)
- 2.1.1과 기능은 동일
- 매개변수 ThreadFactory를 통해 쓰레드 생성과 관련된 로직 설정 가능
부연 설명
- 작업 개수만큼 쓰레드 개수가 늘어난 것을 확인 가능
2.2 단일 크기 쓰레드 풀 생성 메서드
- static ExecutorService newSignleThreadExecutor()
- static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)
2.2.1 static ExecutorService newSignleThreadExecutor()
- 단일 쓰레드를 사용하여 작업을 실행하는 ExecutorService
- 작업들이 순차적으로 실행되는 것을 보장
- ㅅ한 개의 쓰레드가 실행 중에 실패로 인해 종료될 경우 새로운 쓰레드가 생성되어 실패 이후 후속 작업을 대신 실행
- 작업을 순차적으로 실행해야 하거나 여러 작업 간의 순서를 보장해야 할 때 유용
- 쓰레드가 한 개이기 때문에 공유 자원 관리에 대한 복잡성 줄어듦
- newFixedThreadPool(1)과 동등한 기능을 가짐
2.2.2 static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)
- 2.2.1과 기능은 동일
- 매개변수 ThreadFactory를 통해 쓰레드 생성과 관련된 로직 설정 가능
3. 스케줄링 쓰레드 풀 생성
3.1 스케줄링 기능을 지원하는 쓰레드 풀 생성
- static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
- static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)
3.1.1 static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
- 주어진 지연 시간 후에 명령을 실행하거나 주기적으로 실행할 수 있는 쓰레드 풀 생성
- 한 개의 쓰레드가 실행 중에 실패로 인해 종료될 경우 새로운 쓰레드가 생성되어 실패 이후 후속 작업을 대신 실행
- corePoolSize가 0보다 작거나 같을 경우 illegalArgumentException 예외 발생
3.1.2 static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)
- 3.1.1과 기능은 동일
- 매개변수 ThreadFactory를 통해 쓰레드 생성과 관련된 로직 설정 가능
3.2 스케줄링 기능을 지원하는 단일 쓰레드 풀 생성
- static ScheduledExecutorService newSingleThreadScheduledExecutor()
- static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory)
3.2.1 static ScheduledExecutorService newSingleThreadScheduledExecutor()
- 주어진 지연 후에 명령을 실행하거나 주기적으로 실행할 수 있는 단일 쓰레드 풀 생성
- 한 개의 쓰레드가 실행 중에 실패로 인해 종료되었고 종료 이후에도 필요한 경우 새로운 쓰레드가 생성되어 후속 작업 실행
- 작업은 순차적으로 실행되며 항상 하나의 작업만 활성화
- newScheduledThreadPool(1)과 동등한 기능을 가짐
3.2.2 static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory)
- 3.2.1과 기능은 동일
- 매개변수 ThreadFactory를 통해 쓰레드 생성과 관련된 로직 설정 가능
참고
자바 동시성 프로그래밍 [리액티브 프로그래밍 Part.1] - 정수원 강사님
'JAVA > 비동기 프로그래밍' 카테고리의 다른 글
[Java] CompletableFuture (0) | 2024.05.12 |
---|---|
[Java] ThreadPoolExecutor (1) | 2024.04.28 |
[Java] 동기화 도구 (0) | 2024.03.14 |
[Java] Lock, ReentrantLock, ReadWriteLock, ReentrantReadWriteLock (0) | 2024.03.09 |
[Java] synchronized, wait() & notify(), volatile, Deadlock (0) | 2024.03.05 |