Runnable과 Callable의 차이: https://www.notion.so/seolnavy/Runnable-Callable-dae409f85a054578a66dfc88f6f01573
Future 인터페이스는 자바 5에서 공개한 컨커런트 API에 포함되어 있다.RecursiveTask와 RecursiveAction의 명세를 보면,
두 클래스 모두 Future 인터페이스를 구현한 추상 클래스이다.Future 인터페이스에 대한 자바 API 설명을 살펴보면 “비동기 연산의 결과를 표현한다”라고 정의하고 있다.
Future 인터페이스를 이용해서 비동기 연산을 실행하면 저수준의 프로그래밍을 하지 않아도 비동기 처리가 가능하다.
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
isDone과 isCancelled 메서드를 사용해서 비동기 연산이 종료 혹은 취소되었는지 확인할 수 있다.get 메서드를 호출하면 결과값을 응답 받을 때까지 대기한다.import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class FutureExample {
/**
* 제곱을 계산하는 Callable 객체를 생성한다.
*/
public Callable<Long> calSquare(final long value) {
// final Callable<Long> callable = new Callable<Long>() {
// @Override
// public Long call() throws Exception {
// final Long returnValue = value * value;
// TimeUnit.SECONDS.sleep(1);
// System.out.println(value + "의 제곱근은 " + returnValue);
// return returnValue;
// }
// };
// return callable;
return () -> {
final Long returnValue = value * value;
TimeUnit.SECONDS.sleep(1);
System.out.println(value + "의 제곱근은 " + returnValue);
return returnValue;
};
}
public void executeTest() {
// 스레드풀을 생성한다. 고정 스레드 풀을 이용하였다.
final ExecutorService servicePool = Executors.newFixedThreadPool(4);
// Callable 객체를 생성한 후, 스레드 풀에 등록한다.
// 등록된 스레드에 대해 Future 객체를 리턴받는다.
final List<Long> sampleDataList = Arrays.asList(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L);
/*
* 비동기 호출
*/
final List<Future<Long>> futureList = new ArrayList<>();
for (final Long sampleData : sampleDataList) {
final Callable<Long> callable = calSquare(sampleData);
final Future<Long> future = servicePool.submit(callable); // 비동기 실행
futureList.add(future); // 결과처리를 위해 list에 담는다.(여기서 get()을 호출하면 결과응답까지 기다린다(block된다). list에 담아놓고 나중에 꺼낸다)
}
/*
* 비동기 호출결과 get
*/
Long sumValue = 0L;
for (final Future<Long> future : futureList) {
try {
// 결과를 읽어 들일 때까지 대기한다.
// 대기하는 동안, 스레드가 계산을 하고 값을 리턴한다.
sumValue += future.get();
} catch (final InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
System.out.println("최종 합계 : " + sumValue);
// 스레드풀 종료
servicePool.shutdown();
}
public static void main(final String[] args) {
final FutureExample futureExample = new FutureExample();
futureExample.executeTest();
}
}
Callable 인터페이스의 구현체를 실행시키기 위한 스레드 풀을 정의하기 위해 ExecutorService 객체를 생성한다.
// 스레드풀을 생성한다. 고정 스레드 풀을 이용하였다.
final ExecutorService servicePool = Executors.newFixedThreadPool(4);
스레드 풀인 ExecutorService에 Callable 구현체를 등록하고 Future를 리턴받는다.
Future은 비동기 연산 작업의 결과를 확인하는 용도final Callable<Long> callable = calSquare(sampleData); // public Callable<Long> calSquare(final long value)
final Future<Long> future = servicePool.submit(callable);
Future로 연산의 결과를 확인하기 위해 get 메서드를 호출한다.
get 메서드는 비동기 연산이 종료될 때까지 대기한다.
for (final Future<Long> future : futureList) {
try {
// 결과를 읽어 들일 때까지 대기한다.
// 대기하는 동안, 스레드가 계산을 하고 값을 리턴한다.
sumValue += future.get();
} catch (final InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
Runnable과 Callable은 멀티 스레드 기반으로 동작하기 위한 인터페이스이다.
Runnable은 메서드가 void이며, run 메서드만 있기 때문에 실행결과를 리턴하지 않는다.Callable 인터페이스는 제네릭으로 정의한 리턴타입을 가지는 call 메서드가 제공된다.
즉, 비동기로 데이터를 처리한 이후에 그 결과를 리턴할 필요가 있다면 Callable을 이용해야 한다.