Spring Webflux) Future
Date: Updated:카테고리: spring
CompletableFuture
- 2014년, Java 8에서 처음으로 도입
- 비동기 프로그래밍을 지원하는 클래스
Lambda
,Method reference
등 Java 8의 새로운 기능들을 지원Future
인터페이스와CompletionStage
인터페이스를 구현한 클래스Future
인터페이스의 주요 역할- 비동기적인 작업을 수행
- 해당 작업이 완료되면 결과를 반환하는 인터페이스
CompletionStage
인터페이스의 주요 역할- 비동기적인 작업을 수행
- 해당작업이 완료되면 결과를 처리하거나 다른
CompletionStage
를 연결하는 인터페이스, 즉 콜백을 여러개 등록할 수 있다.
ExecutorService
Executor
인터페이스를 상속받았으며 쓰레드 풀을 이용하여 비동기적으로 작업을 실행하고 관리할수 있게끔 지원하는 인터페이스이다.
// Executors 클래스에서 제공하는 Factory method를 사용하여 구현 가능
// 1개로 고정된 사이즈의 단일 ThreadPool 생성
ExecutorService executorService = Executors.newSingleThreadExecutor();
// 10개로 고정된 사이즈의 ThreadPool 생성
ExecutorService executorService = Executors.newFixedThreadPool(10);
// 유동적으로 증가하고 줄어드는 ThreadPool 생성
// 사용 가능한 쓰레드가 없다면 새로 생성해서 작업을 처리하고, 쓰레드가 있다면 재사용한다.
// 쓰레드가 일정시간 사용되지 않으면 회수한다.
ExecutorService executorService = Executors.newCachedThreadPool();
// 10개로 고정된 사이즈의 스케줄링 ThreadPool 생성
// 주기적이거나 지연이 발생하는 작업들을 실행한다.
ExecutorService executorService = Executors.newScheduledThreadPool(10);
// work steal 알고리즘을 사용하는 ForkJoinPool 생성
ExecutorService executorService = Executors.newWorkStealingPool();
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;
}
Future 예시
예시에 사용할 helper 클래스 생성
public class FutureHelper {
// 새로운 쓰레드를 생성하여 1을 반환하는 메서드
public static Future<Integer> getFuture() {
var executor = Executors.newSingleThreadExecutor();
try {
return executor.submit(() -> {
return 1;
})
} finally {
executor.shutdown();
}
}
// 새로운 쓰레드를 생성하고 1초 대기후 1을 반환하는 메서드
public static Future<Integer> getFutureCompleteAfter1s() {
var executor = Executors.newSingleThreadExecutor();
try {
return executor.submit(() -> {
Thread.sleep(1000);
return 1;
});
} finally {
executor.shutdown();
}
}
}
Future.get()
- 결과를 구할때까지 thread가 계속 block 상태로 유지된다.
- 즉, future에서 무한 루프나 오랜시간이 걸린다면 thread가 blocking 상태를 유지한다.
// 비록 결과를 바로 반환 하더라도 상태를 바로 호출했기 때문에 아직 끝나지 않은 상태
Future future = FutureHelper.getFuture();
assert !future.isDone();
assert !future.isCancelled();
// future에 결과값이 담길때 가져오므로 끝난 상태
var result = future.get();
assert result.equals(1);
assert future.isDone();
assert !future.isCancelled();
- 단,
get()
에 매개변수인 timeout 값을 넣어서 호출하게되면 timeout 시간 동안만 thread가 block이 되고 timeout 동안 응답이 반환되지 않을 시TimeoutException
발생
Future future = FutureHelper.getFutureCompleteAfter1s();
var result = future.get(1500, TimeUnit.MILLISECONDS);
assert result.equals(1);
Future futureToTimeout = FutureHelper.getFutureCompleteAfter1s();
Exception exception = null;
try {
// 1초 sleep 상태이므로 0.5초 내로 결과를 반환받지 못함
futureToTimeout.get(500, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
exception = e;
}
assert exception != null;
Future.cancel()
- future의 작업 실행을 취소한다
- 취소할 수 없는 상황이라면
false
를 반환한다. - 매개 변수인
mayInterruptIfRunning
이false
인 경우 시작하지 않은 작업에 대해서만 취소한다.
Future future = FutureHelper.getFuture();
// 작업이 시작하기전에 cancel
var successToCancel = future.cancel(true);
assert future.isCancelled();
assert future.isDone();
// cancel에 의해서 취소됨
assert successToCancel;
// 이미 취소가 된 작업을 또 취소
successToCancel = future.cancel(false);
assert future.isCancelled();
assert future.isDone();
// cancel를 통해 취소된게 아니기 때문에 false 반환
assert !successToCancel;
Future 인터페이스의 한계
- cancel을 제외하고 외부에서 future를 컨트롤 할 수 있는 방법이 없다.
- 반환된 결과를
get()
으로 접근하기 때문에 비동기 처리가 어렵다.get()
은 결과가 나올때까지 block 상태를 유지한다.
- 완료되거나 에러가 발생했는지 구분하기 어렵다.
- 상태를 알수있는 메서드는
isDone()
,isCancelled()
밖에 없으므로 구분할수 없다.
- 상태를 알수있는 메서드는
댓글남기기