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
을 이용해야 한다.