2019.03.14 - [프로그래밍 노트/JAVA] - [JAVA] 블로킹 방식의 작업 완료 통보_1 (스레드풀_2)
2019.03.16 - [프로그래밍 노트/JAVA] - [JAVA] 블로킹 방식의 작업 완료 통보_2 (스레드풀_3)
2019.03.16 - [프로그래밍 노트/JAVA] - [JAVA] 콜백 방식의 작업 완료 통보 (스레드풀_4)
스레드풀
병렬 작업 처리가 많아지면 스레드 수가 증가되고 그에 따른 스레드 생성과 스케줄링으로 인해 CPU가 바빠져서 메모리 사용량이 늘어난다. (애플리케이션 성능 저하)
따라서 스레드의 폭증을 막으려면 스레드풀(ThreadPool)을 사용해야 한다.
스레드 풀은 작업 처리에 사용되는 스레드를 제한된 개수만큼 정해 놓고 작업 큐(Queue)에 들어오는 작업들을 하나씩 스레드가 맡아 처리한다. (작업 요청이 폭증해도 스레드의 전체개수가 늘어나지 않음)
java.util.concurrent 패키지를 사용하여 스레드 풀을 생성할 수 있다.
Executor 클래스
ExecutorService 인터페이스
스레드풀 생성
ExecutorService(스레드 풀) 구현객체는 Executors 클래스 메소드를 통해 간편하게 생성할 수 있다.
메소드명 | 초기 스레드 수 | 코어 스레드 수 | 최대 스레드 수 |
---|---|---|---|
newCachedThreadPool() | 0 | 0 | Integer.MAX_VALUE |
newFixedThreadPool(int num) | 0 | num | num |
초기 스레드 수 : 기본적으로 생성되는 스레드 수
코어 스레드 수 : 최소한으로 유지해야 할 스레드 수
최대 스레드 수 : 스레드풀에서 관리하는 최대 스레드 수
1개 이상의 스레드가 추가되었을 경우 60초 동안 추가된 스레드가 아무 작업을 하지 않으면 추가된 스레드를 종료하고 풀에서 제거한다.
ExecutorService executorService = Executors.newCachedThreadPool();
ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
ExecutorService executorService = new ThreadPoolExecutor(param1, param2, param3, param4, param5);
// param1 : 코어 스레드 개수
// param2 : 최대 스레드 개수
// param3 : 놀고 있는 시간 단위
// param4 : 작업 큐
스레드풀 종료
스레드 풀은 데몬 스레드가 아니기 때문에 main 스레드가 종료되어도 실행 상태로 남아있다.
애플리케이션을 종료하려면 스레드풀을 종료시켜 스레드들이 종료상태가 되도록 처리해주어야 한다.
리턴 타입 | 메소드명(매개변수) | 설명 |
---|---|---|
void | shutdown() | 현재 처리중인 작업, 작업 큐에 대기하는 모든 작업을 처리한 뒤 종료 |
List | shutdownNow() | 작업 중인 스레드를 interrupt해서 작업 중지를 시도하고 스레드 풀을 종료 시킴. 리턴 값은 미처리된 작업(Runnable) 목록 |
boolean | awaitTermination(long timeout, TimeUnit unit) | shotdown() 메소드 호출 후, 모든 작업 처리를 timeout 시간내에 처리하면 true 리턴 못하면 interrupt하고 false 리턴 |
작업 생성과 처리 요청
작업 생성
하나의 작업은 Runnable 혹은 Callable 구현 클래스로 표현한다.
두 클래스의 차이는 작업 처리 완료 후 리턴값이 있냐 없냐의 차이다.
Runnable 구현 클래스
Runnable task = new Runnable(){
@Override
public void run(){
// 작업 내용
}
}
Callable 구현 클래스
Callable<T> task = new Callable<T>(){
@Override
public T call() throws Exception{
// 작업 내용
return T;
}
}
call() 메소드는 리턴값이 존재한다. call의 리턴타입은 implements Callable 에서 지정한 T 타입이다.
스레드풀의 스레드는 작업 큐에서 Runnable 또는 Callable 객체를 가져와 run()과 call() 메소드를 실행한다.
작업 처리 요청
작업 처리 요청이란 ExecutorService의 작업 큐에 Runnable 또는 Callable 객체를 넣는 행위를 말한다.
ExecutorService 작업을 처리하기위해 아래 두가지 메소드를 제공한다.
리턴 타입 | 메소드명(매개 변수) | 설명 |
---|---|---|
void | execute(Runnable command) | - Runnable을 작업 큐에 저장 - 작업 처리 결과를 받지 못함 |
Future<?> Future Future |
submit(Runnable task) submit(Runnable task, V result) submit(Callable task) |
- Runnable 또는 Callable을 작업 큐에 저장 - 리턴된 Future를 통해 작업 처리 결과를 얻을 수 있음 |
execute()는 작업 처리 결과를 받지 못하고 submit()은 작업 처리 결과를 받을 수 있도록 Future를 리턴한다.
execute()는 작업 처리 도중 예외가 발생하면 스레드가 종료되고 스레드 풀에서 제거된다. (다른 작업을 하기 위해선 새로운 스레드 생성)
submit()은 작업 처리 도중 예외가 발생하여도 종료되지 않고 재사용된다.
그렇기 때문에 오버헤드를 줄이기 위해 submit() 을 사용하는 것이 좋다.
public class ExecuteExample {
public static void main(String[] args) throws Exception{
ExecutorService executorService = Executors.newFixedThreadPool(2); // 최대 스레드가 2개인 스레드풀 생성
for(int i=0; i<10; i++){
Runnable runnable = new Runnable() {
@Override
public void run() {
// 스레드 총 개수 및 작업 스레드 이름 출력
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executorService;
int poolSize = threadPoolExecutor.getPoolSize();
String threadName = Thread.currentThread().getName();
System.out.println("[총 스레드 개수 : " + poolSize + "] 작업 스레드 이름 : " + threadName);
// 예외 발생시킴
int value = Integer.parseInt("삼");
}
};
//executorService.execute(runnable);
executorService.submit(runnable);
Thread.sleep(10);
}
executorService.shutdown();
}
}
submit() 결과
[총 스레드 개수 : 1] 작업 스레드 이름 : pool-1-thread-1
[총 스레드 개수 : 2] 작업 스레드 이름 : pool-1-thread-2
[총 스레드 개수 : 2] 작업 스레드 이름 : pool-1-thread-2
[총 스레드 개수 : 2] 작업 스레드 이름 : pool-1-thread-2
[총 스레드 개수 : 2] 작업 스레드 이름 : pool-1-thread-2
[총 스레드 개수 : 2] 작업 스레드 이름 : pool-1-thread-1
[총 스레드 개수 : 2] 작업 스레드 이름 : pool-1-thread-2
[총 스레드 개수 : 2] 작업 스레드 이름 : pool-1-thread-1
[총 스레드 개수 : 2] 작업 스레드 이름 : pool-1-thread-2
[총 스레드 개수 : 2] 작업 스레드 이름 : pool-1-thread-1
execute()는 직접 해보시길, 예외가 발생하고 thread가 계속 재생성되는 것을 볼 수 있음 (스레드 이름이 바뀜)
'프로그래밍 노트 > JAVA' 카테고리의 다른 글
[JAVA] 블로킹 방식의 작업 완료 통보_2 (스레드풀_3) (0) | 2019.03.16 |
---|---|
[JAVA] 블로킹 방식의 작업 완료 통보_1 (스레드풀_2) (0) | 2019.03.14 |
[JAVA] 스레드 그룹(thread group) (0) | 2019.03.13 |
[JAVA] interrupt() 메소드를 이용하여 스레드 정지시키기 (0) | 2019.03.04 |
[JAVA] 스레드간 협업(wait, notify, notifyAll) (0) | 2019.03.04 |