chapter12. 멀티 스레드. 12.9 스레드풀


* 스레드 폭증

- 병렬 작업 처리가 많아지면 스레드의 개수가 증가

- 스레드 생성과 스케쥴링으로 인해 CPU가 바빠지고, 메모리 사용량이 늘어난다

- 따라서 애플리케이션의 성능이 급격히 저하된다.


* 스레드풀(Thread Pool)

- 작업 처리에 사용되는 스레드를 제한된 개수만큼 미리 생성

- 작업 큐(Queue)에 들어오는 작업들을 하나씩 스레드가 맡아 처리

- 작업 처리가 끝난 스레드는 작업 결과를 애플리케이션으로 전달

- 스레드는 다시 작업큐에서 새로운 작업을 가져와 처리


* ExecutorService 인터페이스와 Executors 클래스

- 스레드풀을 생성하고 사용할 수 있도록 java.util.concurrent 패키지에서 제공

- Executors의 정적 메소드를 이용해서 ExecutorService 구현 객체 생성

- 스레드풀 = ExecutorService 객체


* ExecutorService 동작 원리



12.9.1 스레드풀 생성 및 종료

* 스레드풀 생성

- 다음 두가지 메소드 중 하나로 간편 생성


- newCachedThreadPool()

// int값이 가질 수 있는 최대 값만큼 스레드가 추가되나, 운영체제의 상황에 따라 달라진다.

// 1개 이상의 스레드가 추가되었을 경우

// 60초 동안 추가된 스레드가 아무 작업을 하지 않으면

// 추가된 스레드를 종료하고 풀에서 제거한다


- newFixedThreadPool(int n Threads)

// 코어 스레드 개수와 최대 스레드 개수가 매개값으로 준 nThread이다.

// 스레드가 작업을 처리하지 않고 놀고 있더라도 스레드 개수가 줄지 않는다


- ThreadPoolExecutor을 이용한 직접 생성

// newCachedThreadPool()과 newFixedThreadPool(int n Threads)가 내부적으로 생성

// 스레드의 수를 자동을 관리하고 싶을 경우 직접 생성해서 사용

ex)

// 코어 스레드 개수가 3, 최대 스레드 개수가 100인 스레드풀을 생성

// 3개를 제외한 나머지 추가된 스레드가 120초 동안 놀고 있을 경우

// 해당 스레드를 제거해서 스레드 수를 관리



* 스레드풀 종료

- 스레드풀의 스레드는 기본적으로 데몬 스레드가 아니다.

// main 스레드가 종료되더라도 스레드풀의 스레드는 작업을 처리하기 위해 계속 실행되므로

// 애플리케이션은 종료되지 않는다

// 따라서 스레드풀을 종료해서 모든 스레드를 종료시켜야 한다.




12.9.2 작업 생성과 처리 요청

* 작업 생성

- 하나의 작업은 Runnable 또는 Callable 객체로 표현한다


- Runnable과 Callable의 차이점

// 작업 처리 완료 후 리턴값이 있느냐 없느냐



- 스레드풀에서 작업 처리

// 작업 큐에서 Runnable 또는 Callable 객체를 가져와

// 스레드로 하여금 run()과 call() 메소드를 실행토록 하는 것이다.


* 작업 처리 요청

ExecutorService의 작업 큐에 Runnable 또는 Callable 객체를 넣는 행위를 말한다.

- 작업 처리 요청을 위해 ExecutorService는 다음 두 가지 종류의 메소드를 제공한다



- 작업 처리 도중 예외가 발생할 경우

// execute()

: 스레드가 종료되고 해당 스레드는 제거된다

: 따라서 스레드풀은 다른 작업 처리를 위해 새로운 스레드를 생성한다.


// submit()

: 스레드가 종료되지 않고 다음 작업을 위해 재사용된다.


execute() 메소드로 작업 처리 요청한 경우

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package sec09.exam01_execute_submit;
 
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
 
public class ExecuteVsSubmitExample {
    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(); // 스레드풀 종료
    }
}

cs


12.9.3 블로킹 방식의 작업 완료 통보


- Future

// 작업 결과가 아니라 지연 완료(pending completion) 객체

// 작업이 완료될때까지 기다렸다가 최종 결과를 얻기위해서 get() 메소드를 사용



Posted by 너래쟁이
: