본문 바로가기

Java 공부

12장 스레드풀(3)


리턴값이 있는 작업 완료 통보



Callable<T> task = new Callable<T>() {

@Override

public T call() throws Exception {

//스레드가 처리할 작업 내용

return T;

}

};

Future<T> future = executorService.submit(task)

try {

T result = future.get();

} catch (InterruptedException e) {

//작업 처리 도중 스레드가 interrupt될 경우 실행할 코드

} catch(ExecutionException e) {

//작업 처리 도중 예외가 발생된 경우 실행할 코드

}



1) 리턴값이 있는 작업을 정의할 때는 Callable 객체를 사용한다. <T>에는 리턴값의 타입을 선언해 놓는다.

2) Callable이 가지고 있는 call메소드를 재정의 해서 스레드가 처리할 작업 내용을 적어준다.

3)  executorService.submit(task)로 작업 객체를 작업 큐에 넣어주고, submit메소드는 그 즉시 Future라는 객체를 리턴한다.

    Future<T> future에서 <T>는 future가 나중에 얻게될 결과값의 타입을 말한다. 

   결국 Callable<T>와 Future<T>는 같은 타입이어야 하는 것.

4) future.get()를 호출하면 스레드 풀의 스레드가 call()메소드를 다 실행할 때까지, 메인스레드는 블로킹이 된다(기다림)

   이후 스레드 풀의 스레드가 call()메소드를 다 실행하게 되면 get()메소드는 return T로 인해 결과값 T를 리턴한다.


예제로 더 쉽게 이해 해보자

*예제로 알 수 있는것

--> 결과 값이 필요한 작업을 정의할 때에는 Callable객체를 이용한다.

--> Callable의 타입 파라미터가 call()메소드의 리턴타입, Future 타입 파라미터, get()메소드의 리턴타입이 된다는 것이다.

--> 또한 현재 예제에서 future.get()메소드의 리턴타입은 Integer인데 int로 받은 이유는 자동 언박싱으로 처리가 가능하기 때문이다.


작업 처리 결과를 외부 객체에 저장

-스레드1 : 1반~6반까지의 성적의 총합을 구하고, 스레드2: 7반~12반까지의 성적의 총합을 구한다고 가정한다.

  이때, 사용자는 1반부터 12반까지의 총합을 얻고싶다면 외부 객체인 Result에 각각의 스레드가 만든 결과를 누적하여 원하는 값을 얻을 수 있다.

-결국 Result객체는 스레드들이 공유하여 결과값을 누적해야하는 객체로 공유객체가 된다.

-사용방법

Result result = ...;

Runnable task = new Task(result);        //작업에서 쓸 수 있도록 result객체를 매개값으로 넣어준다.

Future<Result> future = executorService.submit(task, result); 

result = future.get();

* result 타입이 모두 동일해야 한다.


-Runnable 클래스를 구현한 Task객체

class Task implements Runnable {

Result result;

Task(Result result) { this.result = result; }

@Override

public void run() {

//작업 코드

//처리 결과를 result 저장

}

}



예제를 통하여 자세히 알아보자.

//공유 객체로 쓰일 Result 클래스 선언                        ---------------------1

class Result {

int accumValue;

synchronized void addValue(int value) {

accumValue +=value;

}

}


public class ResultByRunnableExample {

public static void main(String[] args) {


//스레드 풀 생성                            ---------------------2

ExecutorService executorService = Executors.newFixedThreadPool(

Runtime.getRuntime().availableProcessors()

);

System.out.println("작업 처리 요청");


//작업 정의 : 작업 객체를 생성하는 클래스 정의                             ---------------------3

class Task implements Runnable {

Result result;

Task(Result result) {

this.result = result;

}

@Override

public void run() {

int sum = 0;

for(int i=1; i<=10; i++) {

sum += i;

}

result.addValue(sum);

}

}

}

//공유 객체(외부 객체)

Result result = new Result();

//서로 다른 두 개의 작업을 정의                             ---------------------4

Runnable task1 = new Task(result);

Runnable task2 = new Task(result);

//두개의 작업을 작업 큐에 넣는다.

Future<Result> future1 = executorService.submit(task1, result);

Future<Result> future2 = executorService.submit(task2, result);


//메인 스레드가 task1과 task2가 실행을 완료할 때까지 블로킹하여 기다린다.

try {

result = future1.get();                // ---------------------5

result = future2.get();

System.out.println("처리 결과: " + result.accumValue);

  System.out.println("작업 처리 완료");

}     catch (Exception e) {

System.out.println("실행 예외 발생함 "+ e.getMessage());

}


}

}




*보충설명

1) 공유객체로 쓰일 Result 클래스를 정의한다.

    스레드1과 스레드2의 결과 값을 누적 시킬 Result객체이므로, 결과 값을 누적시킬 변수(accumValue)를 지정하고, 

    결과 값을 누적시키는 함수(addValue)를 정의한다.

    이때, 결과 값을 누적시키는 함수 addValue는 하나의 스레드만 이용할 수 있게끔 동기화 메소드(synchronized)로 정의하도록 한다.


2) 메인 스레드를 가지고 있는 ResultByRunnable 클래스의 메인함수에 스레드 풀을 생성한다.

    Runtime.getRuntime().availableProcessors()   : 현재 프로그램이 실행되고 있는 CPU의 코어의 개수를 뜻하며,

    스레드 풀은 CPU가 가지고 있는 코어의 개수만큼의 스레드를 가지게 된다.


3) 이전의 예제에서는 곧바로 Runnable의 객체를 만들어서 작업내용을 정의 했지만 이번 예제에서는 작업 2개를 만들어야 하기 때문에

   작업 내용을 정의하는 클래스(틀)를 만들고, 그 클래스의 객체로 작업내용을 정의하고, 작업을 만들도록 한다.


4) 3)에서 만들어 놓은 Task 클래스를 객체로 하는 서로 다른 작업을 정의 한다.

    task1과 task2는 서로 다른 객체, 매개변수로 주어진 result는 같은 객체


5) result는 같은 객체이다. 

   하지만   result = future1.get(); 의 result는 task1의 결과값만 가지고 있는 result 이고,

   result = future2.get();  의 result는 task2의 결과값까지 누적되어진 result이다.



본 포스팅은 이것이 자바다 책을 참고하여 작성하였습니다.

'Java 공부' 카테고리의 다른 글

13장 제네릭(Generic)  (0) 2019.01.20
12장 스레드풀(4)  (0) 2018.12.12
12장 스레드풀(2)  (0) 2018.12.11
12장 스레드풀(1)  (0) 2018.12.09
12장 스레드 그룹  (0) 2018.12.09