Callable과 Future
Callable
과Runnable
의 차이점은 작업의 결과를 받을 수 있다는 사실이다.Future
는 비동기적인 작업의 현재 상태를 조회하거나 결과를 가져올 수 있다.
다음은 Future
에 대해 설명한 API 주석이다.
Future
는 비동기식 계산의 결과를 나타냅니다.- 계산이 완료되었는지 확인하고, 완료되기를 기다리며, 결과를 확인할 수 있는 방법이 제공됩니다.
- 결과는 계산이 완료된 경우에만 메서드
get
을 사용하여 검색할 수 있으며, 작업이 완료될 때까지 블록킹 됩니다. - 또한 작업이 정상적으로 완료되었는지 또는 취소되었는지 확인할 수 있는 추가적인 방법이 제공된다.
- 작업이 완료되면 이를 취소할 수 없다.
get() - 결과를 가져오기
get()
은 오버로딩 된 두가지 메서드를 제공한다.
- 계산이 완료될 때까지 기다린 다음에 결과를 검색한다.
- 최대 지정된 시간까지 기다렸다가 사용 가능한 경우 결과를 검색합니다.
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;
public class Main {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Callable<String> hello = () -> {
Thread.sleep(2000L);
return "Hello";
};
Future<String> helloFuture = executorService.submit(hello);
System.out.println("Started!");
helloFuture.get(); // blocking call
System.out.println("End!!");
executorService.shutdown();
}
}
-
다음과 같은 경우에는 터미널에
Started!
라는 글씨가 적히고 나서, 2초간 대기를 한 후에End!!
라는 글씨가 찍히게 된다. -
타임아웃(최대로 기다릴 시간)을 설정할 수 있다.
isDone(), isCancelled() - 작업 상태 확인하기
isDone
은 작업이 완료되었으면 참을 반환하고 아닌 경우에는 거짓을 반환한다.isCancelled
는 작업이 완료된 경우 참을 반환한다. 작업이 정상적으로 종료되거나, 예외 또는, 취소된 경우도 역시 참으로 반환된다.
cancel() - 작업 취소하기
- 작업을 취소할 때 사용한다.
- 작업이 이미 완료되었거나, 취소되었거나, 다른 이유로 취소할 수 없는 경우에는 실패한다.
- 이 메서드가 실행된 후에
isDone()
는 항상 참을 반환한다. - 일반적으로 작업이 이미 정상적으로 완료되었기 때문에, 작업을 취소할 수 없는 경우에는 거짓을 반환하고 그렇지 않으면 참을 반환한다.
- 성공적으로 취소했으면, 참 아니면 거짓을 반환한다.
invokeAll()과 invokeAny()의 차이점
invokeAll
- 태스크를 실행하고 모두 완료되거나 시간 초과가 만료될 때, 상태 및 결과를 저장하고 있는
Future
목록을 반환한다. Future.isDone
은 반환된 목록의 각 요소에 대해서 적용됩니다.- 반환시에 완료되지 않은 태스크는 취소됩니다.
- 이 작업이 진행되는 동안 지정한 컬렉션이 수정되면 메서드의 결과가 정의되지 않습니다.
- 동시에 실행한 작업중에 가장 오래걸리는 작업만큼 시간이 소요된다.
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;
public class Main {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Callable<String> hello = () -> {
Thread.sleep(2000L);
return "Hello";
};
Callable<String> java = () -> {
Thread.sleep(4000L);
return "Java";
};
Callable<String> dongwook = () -> {
Thread.sleep(100L);
return "Dong Wook";
};
List<Future<String>> futures = executorService.invokeAll(Arrays.asList(hello, java, dongwook));
for (Future<String> f : futures) {
System.out.println(f.get()); // Hello, Java, Dong Wook 순서대로 출력된다.
}
executorService.shutdown();
}
}
invokeAny
- 성공적으로 완료된 태스크의 결과를 반환합니다.
- 이 작업이 진행되는 동안 지정된 컬렉션이 수정되면 이 메서드의 결과가 정의되지 않는다.
- 동시에 실행한 작업중에 제일 짧게 걸리는 작업만큼 시간이 걸린다.
- 이 역시 또한 블록킹 콜이다.
public class Main {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newFixedThreadPool(4);
Callable<String> hello = () -> {
Thread.sleep(2000L);
return "Hello";
};
Callable<String> java = () -> {
Thread.sleep(4000L);
return "Java";
};
Callable<String> dongwook = () -> {
Thread.sleep(100L);
return "Dong Wook";
};
String futures = executorService.invokeAny(Arrays.asList(hello, java, dongwook));
System.out.println(futures); // Dong Wook
executorService.shutdown();
}
}