[JAVA] Future 인터페이스

Future은 비동기 계산의 결과를 나타냅니다. 이를 통해 계산이 완료되었는지 확인하고, 완료를 기다리며 계산 결과를 검색할 수 있는 메서드가 제공됩니다. 
계산이 완료된 경우에만 get 메서드를 사용하여 결과를 검색할 수 있으며, 필요한 경우 준비될 때까지 블로킹됩니다.

작업을 취소하려면 cancel 메서드를 사용합니다. 
작업이 정상적으로 완료되었는지 아니면 취소되었는지를 확인하기 위한 추가적인 메서드도 제공됩니다. 한번 계산이 완료되면 작업을 취소할 수 없습니다. 
작업의 결과를 사용하지 않고 취소 가능성을 위해 Future를 사용하려면 Future<?> 형식을 선언하고 기본 작업의 결과로 null을 반환할 수 있습니다.

 

Future 메서드

  • cancel(boolean) : 작업을 취소하려고 시도합니다. 작업이 이미 완료된 경우이거나 취소를 했을 경 실패합니다. 성공하면 작업이 실행되지 않습니다. 작업이 이미 시작되었으면 매개변수를 통해 작업을 실행할지 중지할지 결정됩니다. ca메서드가 실행된 후 isDone 메서드를 호출하면 항상 true를 반환합니다.
  • isCancelled() : 작업이 정상적으로 취소되면 true 반환합니다.
  • isDone() : 작업이 완료되면 true를 반환합니다. 정상적인 종료 또는 cancel되거나 예외상태로 true를 반환됩니다.
  • get() : 작업이 완료될때까지 기다린 후 결과를 반환합니다.
  • get(long, TimeUnit) : 시간만큼 대기했다가 안되면 TimeoutException을 보냅니다.

cancel 예시

Runnable task = () -> {
    System.out.println("task 실행");
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(Thread.currentThread().getName());
};

ExecutorService es = Executors.newFixedThreadPool(1);
Future<?> future = es.submit(task);
future.cancel(false);
es.shutdown();

cancel 매개변수에 false를 넘겨주면서 작업이 시작되었으면 실행됩니다. 그래서 인터럽트가 걸리지 않습니다. 하지만 true일때는 작업이 시작되도 종료되기 때문에 인터럽트가 실행됩니다.

 

isCancelled 예시

Runnable task = () -> {
    System.out.println("task 실행");
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("task 끝");
};

ExecutorService es = Executors.newSingleThreadExecutor();
Future<?> future = es.submit(task);
boolean isCancel = future.isCancelled();
System.out.println("isCancel:" + isCancel); // false
es.shutdown();

cancel를 했을 경우

Runnable task = () -> {
    System.out.println("task 실행");
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("task 끝");
};

ExecutorService es = Executors.newSingleThreadExecutor();
Future<?> future = es.submit(task);
future.cancel(false);
boolean isCancel = future.isCancelled();
System.out.println("isCancel:" + isCancel); // true
es.shutdown();

 

isDone 예시

Runnable task = () -> {
    System.out.println("task 실행");
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("task 끝");
};

ExecutorService es = Executors.newSingleThreadExecutor();
Future<?> future = es.submit(task);
boolean isDone = future.isDone();
System.out.println("isDone:" + isDone); // false
es.shutdown();
ExecutorService es = Executors.newSingleThreadExecutor();
Future<?> future = es.submit(task);
try {
    Thread.sleep(2000);
} catch (InterruptedException e) {
    e.printStackTrace();
}
boolean isDone = future.isDone();
System.out.println("isDone:" + isDone); // true

task가 1초정도 걸리니 2초뒤에 isDone을 호출하면 true가 나옵니다.

ExecutorService es = Executors.newSingleThreadExecutor();
Future<?> future = es.submit(task);
future.cancel(false);
boolean isDone = future.isDone();
System.out.println("isDone:" + isDone); //true
es.shutdown();

cancel를 하면 무조건 isDone은 true가 나옵니다.

 

get 예시

Callable<String> task = () -> {
    System.out.println("task 실행");
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "task 끝";
};

ExecutorService es = Executors.newSingleThreadExecutor();
Future<String> future = es.submit(task);
String result = null;
try {
    result = future.get();
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}
System.out.println("result:" + result); //task 끝
es.shutdown();

Callable를 통해 비동기적으로 리턴값을 주면 future.get에서 값을 기다려서 리턴을 해줍니다.

Callable<String> task = () -> {
    System.out.println("task 실행");
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "task 끝";
};

ExecutorService es = Executors.newSingleThreadExecutor();
Future<String> future = es.submit(task);
String result = null;
try {
    result = future.get(1, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
} catch (TimeoutException e) {
    e.printStackTrace();
}
System.out.println("result:" + result);
es.shutdown();

task는 5초 걸리는데 get에 최대 시간을 1초로 잡고 코드를 실행시켜서 TimeoutException이 나면서 result로 초기값인 null이 나옵니다.