045 Future

강의 메모 - Future 구조 및 API - 1, 2, 3 #

개요 #

  • 자바에서 Future 는 비동기 작업의 결과를 나중에 가져올 수 있도록 도와주는 인터페이스이다
  • Future 는 비동기 작업이 완료되었는지 여부를 확인할 수 있고 조건에 따라 작업을 취소할 수 있으며 작업의 결과를 얻는 방법을 제공한다
  • Future 는 작업의 결과를 가져올 때까지 블로킹되며 여러 작업을 조합하는 문제, 예외 처리의 어려움 등이 존재하는데 이런 단점을 보완하기 위해 자바 8부터는 CompletableFuture 와 같은 개선된 비동기 도구들이 제공되고 있다 img.png

Future & Callable #

  • Future 와 Callable 은 자바에서 비동기 작업을 처리하기 위해 서로 관련된 관계를 가진 클래스들이다
    • Callable 은 비동기 작업의 실행 내용을 정의하는 인터페이스로서 call() 메서드를 구현하여 작업을 정의하고 작업의 결과를 반환한다
    • Future 은 Callable 에서 반환한 비동기 작업의 결과를 가져오기 위한 인터페이스로서 작업이 완료될 때까지 결과를 기다리거나 작업이 완료되면 결과를 가져올 수 있다
  • ExecutorService 의 submit() 메서드는 Callable 을 받아 작업을 실행하고 Future를 반환 한다 img_1.png

구조 #

// 작업이 이미 완료되었거나 취소되었거나 다른 이유로 취소할 수 없는 경우 => 아무런 일도 발생하지 않으며 false 를 반환한다
// 작업이 시작되지 않은 경우 => 해당 작업은 실행되지 않으며 true 를 반환한다
// 작업이 이미 시작된 경우 => mayInterruptIfRunning 매개변수에 따라 결정된다
     1) mayInterruptIfRunning 파라미터가 true 인 경우 => 해당 작업을 중지시키기 위해 현재 작업을 실행 중인 스레드를 인터럽트 한다. 작업결과를 가져 올 때 취소 예외가 발생한다
     2) mayInterruptIfRunning 파라미터가 false 인 경우 => 진행 중인 작업을 완료 할 수 있다. 작업결과를 가져 올 때 취소 예외가 발생한다
// 이 메서드의 반환 값은 작업이 현재 취소되었는지 여부를 반드시 나타내지 않기 때문에 정확한 결과는 isCancelled 메서드를 사용해야 한다
// 작업과 연관된 모든 대기 중인 스레드를 깨우고 시그널을 보내며 done()을 호출하고 callable을 null로 설정한다
// future.get() 하여 블로킹된 모든 스레드를 꺠우는것 (작업을 취소했으므로)
boolean cancel(boolean mayInterruptIfRunning);

// 이 작업이 정상적으로 완료되기 전에 취소되었으면 true 를 반환한다. 취소되었는지 여부를 명확하게 확인할 때 사용함
boolean isCancelled() 

// 작업이 완료된 경우 true를 반환하며 완료는 정상 종료, 예외 또는 취소를 포함한 모든 경우에 true를 반환한다
boolean isDone();

// 작업 결과를 반환하며 작업이 완료될 때 까지 스레드는 대기한다. 
// 작업이 예외를 던져 실패하면 해당 예외가 Future.get()을 호출할 때 발생한다. 실패했을 경우 실패한 정보나 결과값을 Future 에서 얻어 올 수 없고 예외 구문에서 확인해야 한다
// 예외 발생: CancellationException – 작업이 취소된 경우,  ExecutionException – 작업이 예외를 발생시킨 경우,  InterruptedException – 현재 스레드가 대기 중에 인트럽트 된 경우
V get() throws InterruptedException, ExecutionException

// 지정한 시간 동안 대기하고 해당 시간 내에 완료되면 작업결과를 반환하고 시간 내에 완료되지 않으면 TimeoutException 예외를 발생시킨다
V get(long timeout, TimeUnit unit)
  • FutureTask : 구현체 img_2.png
private volatile int state;  // 작업 상태 변수
private static final int NEW          = 0;  // 작업 시작

private static final int COMPLETING   = 1; // 작업진행 중

private static final int NORMAL       = 2;  // 작업 완료

private static final int EXCEPTIONAL  = 3;  // 예외 발생

private static final int CANCELLED    = 4;  // 작업 취소

private static final int INTERRUPTING = 5;  // 인트럽트 중

private static final int INTERRUPTED  = 6;  // 인트럽트 됨
  • 작업완료는 set, setException, cancel 메소드에서만 이루어진다
    • set() : 정상적인 결과 저장을 위함
    • setException() : 발생한 오류 저장
    • cancel() : 작업 취소
  • 작업에 따른 상태 변화는 다음과 같다
    • 정상완료: NEW > COMPLETING > NORMAL
    • 예외발생: NEW > COMPLETING > EXCEPTIONAL
    • 작업취소: NEW > INTERRUPTING > INTERRUPTED // 작업취소 인자가 true 인 경우
      • 해당 작업을 중지시키기 위해 현재 작업을 실행 중인 스레드를 인터럽트 한다. 작업결과를 가져 올 때 취소 예외가 발생한다
    • 작업취소: NEW > CANCELLED // 작업취소 인자가 false 인 경우
      • 진행 중인 작업을 완료 할 수 있다. 작업결과를 가져 올 때 취소 예외가 발생한다
  • get() 실행 시 반환하는 결과 값
    • 상태가 NORMAL 인 경우 : 정상 값 반환
    • 상태가 CANCELLED, INTERRUPTING, INTERRUPTED 인 경우 CancellationException 예외 던짐
    • 그 외 모든 상태는 ExecutionException 과 timeout 이 지정된 경우 TimeoutException 을 던짐

작업 취소 흐름도 #

  • Future 에서 Callable 속성을 가지고있음
  • interrupt() 되면 인터럽트 상태
    • mayInterruptIfRunning 파라미터가 true 인 경우 => 해당 작업을 중지시키기 위해 현재 작업을 실행 중인 스레드를 인터럽트 한다. 작업결과를 가져 올 때 취소 예외가 발생한다
    • 이 과정에서 아래 흐름을 보면됨
  • Callable 의 call() 실행했는데 여기서 sleep(), wait()이 포함되어있는가?
    • 포함 : 작업 중단 (InterruptedException 발생)
    • 포함X : 작업을 중단하지 않음, 계속 작업 진행 (interrupt 에 반응하지 않기 때문)

img_3.png

  • compareAndSet() : NEW 이면 세번째 매개변수로 치환해라
  • 현재 수행중인 쓰레드가 null이 아니면 현재 쓰레드에 인터럽트함
  • finally 에서 인터럽트를 걸었으니 INTERRUPTED 상태로 변경