병렬 작업 처리가 많아지면 스레드 개수가 증가하고, 그에 따른 스레드 생성과 스케줄링으로 인해 CPU가 바빠져 메모리 사용량이 늘어난다. -> 애플리케이션 성능 저하
👉갑작스런 병렬 작업의 폭증으로 인한 스레드의 폭증을 막기위해 스레드풀(ThreadPool)사용
스레드풀은 작업 처리에 사용되는 스레드를 제한된 개수만큼 정해놓고 작업 큐(Queue)에 들어오는 작업들을 하나씩 스레드가 맡아 처리한다. 작업 처리가 끝난 스레드는 다시 작업 큐에서 새로운 작업을 가져와 처리함. 때문에 작업 처리 요청이 폭증하여도 스레드의 전체 개수가 늘어나지는 않기 때문에 애플리케이션의 성능이 급격히 저하되지는 않는다.
ExecutorService 인터페이스와 , Executors 클래스를 사용해 구현
메소드명 | 초기 스레드 수 | 코어 스레드 수 | 최대 스레드 수 |
---|---|---|---|
newCachedThreadPool() | 0 | 0 | integer.MAX_VALUE |
newFixedThreadPool(int nThreads) | 0 | nThreads | nThreads |
초기 스레드수와 코어 스레드 수가 0이고, 스레드 개수가 작업 개수보다 많으면 새 스레드를 생성시켜 작업을 처리한다. 1개 이상의 스레드가 추가되었을 경우, 60초 동안 추가된 스레드가 아무 작업을 하지 않으면 추가된 스레드를 종료하고 풀에서 제거한다.
🔻생성방법
ExecutorService executorService = Executors.newCachedThreadPool();
이 스레드풀은 스레드가 작업을 처리하지 않고 놀고 있더라도 스레드 개수가 줄지 않는다.
🔻생성방법
ExecutorService executorService = Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors()
);
스레드풀의 스레드는 기본적으로 main 스레드가 종료되더라도 작업을 처리하기 위해 계속 실행 상태로 남아있다.
따라서 애플리케이션을 종료하려면 스레드풀을 종료시켜 스레드풀이 종료 상태가 되도록 처리해주어야 한다.
리턴타입 | 메소드명(매개변수) | 설명 |
---|---|---|
void | shutdonw() | 현재 처리중인 작업뿐만 아니라 작업 큐에 대기하고 있는 모든 작업을 처리한 뒤에 스레드 풀을 종료시킨다. |
List(Runnable) | shutdownNow() | 현재 작업 처리 중인 스레드를 interrupt해서 작업 중지를 시도하고 스레드풀을 종료시킨다. 리턴 값은 작업 뉴에 있는 미처리 된 작업(Runnable)의 목록이다. |
boolean | awaitTermination(long tmieout,TimeUint unti) | shutdown() 메소드 호출 이후, 모든 작업 처리를 timeout 시간 내에 완료하면 true를 리턴하고, 완료하지 못하면 작업 처리 중인 스레드를 interrupts하고 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;
}
}
ExecutorService의 작업 큐에 Runnable 또는 Callable 객체를 넣는 행위
리턴 타입 | 메소드명(매개변수) | 설명 |
---|---|---|
void | execute(Runnable command) | Runnable을 작업 큐에 저장 / 작업 처리 결과를 받지 못함 |
Future<?> | submit(Runnable task) | Runnable 또는 Callable을 작업 큐에 저장 / 리턴 된 Future를 통해 작업 처리 결과를 얻을 수 잇음 |
submit(Runnable task, V result) | ||
subit(Callable task) |
//새로운 스레드를 생성해서 호출
new Thread(new Runnable(){
@Override
public void run(){
try{
future.get();
}catch(Exception e){
e.printStackTrace();
}
}
}).start();
//스레드풀의 스레드가 호출
executorService.submit(new Runnable(){
@Override
public void run(){
try{
future.get();
}catch(Exception e){
e.printStackTrace();
}
}
});
리턴타입 | 메소드명(매개변수) | 설명 |
---|---|---|
boolean | cancle(boolean mayInterruptIfRunning) | 작업 처리가 진행중일 경우 취소시킴 |
boolean | isCancelled() | 작업이 취소되었는지 여부 |
boolean | isDone() | 작업 처리가 완료 되었는지 여부 |
Future future = executorService.submit(task);
try{
future.get();
}catch(InterruptedExeption e){
}catch(ExecutionException e){}
public class NoResultExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors()
);
System.out.println("[작업처리요청]");
Runnable runnable = new Runnable() {
@Override
public void run() {
int sum = 0;
for (int i = 1; i <= 10; i++) {
sum += i;
}
System.out.println("[처리결과] " + sum);
}
};
Future future = executorService.submit(runnable);
future.get();
System.out.println("[작업처리완료]");
}
}
Future<T> future = executorService.submit(task)
public class ResultByCallableExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors());
System.out.println("[작업처리요청]");
Callable<Integer> task = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= 10; i++) {
sum += 1;
}
return sum;
}
};
Future<Integer> future = executorService.submit(task);
int sum = future.get();
System.out.println("[처리결과]" +sum);
System.out.println("[작업처리완료]");
executorService.shutdown();
}
}
poll()
과 take()
메소드 제공executorService.submit(new Runnable){
@Override
public void run(){
while(true){
//완료된 작업이 잇을때까지 블로킹, 완료된 작업이 있으면 Future를 리턴
Future<Integer> future = completionService.take();
//get()은 블로킹 되지 않고 바로 작업 결과를 리턴
int value = future.get();
System.out.println(value);
}
}
}
콜백방식 : 애플리케이션이 스레드에 작업 처리를 요청한 후, 스레드가 작업을 완료하면 특정 메소드를 실행하는 기법
콜백방식은 작업 처리를 요청한 후 결과를 기다릴 필요없이 다른 기능을 수행할 수 있다.
작업 처리가 완료 되면 자동적으로 콜백 메소드가 실행되서 결과를 알 수 있기 때문!
ExecutorService는 콜백을 위한 별도의 기능을 제공하지 X
Runnable 구현 클래스를 작성할때 콜백 기능을 구현 할 수 있다. 직접 정의하거나 java.nio.channels.CompletionHandler 이용 (비동기 통신의 콜백 객체를 만들때 사용)
<CompletionHandler<v,A> callback = new Completionhandler<V,A>(){
@Override
public void completed(V result, A attachment){}
@Override
public void fialed(V result, A attachement){}
}