ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • CompletableFuture (3) - Callable 과 Future
    프로그래밍/Java 2022. 6. 14. 15:18

    Thread는 Runnable과 Callable의 구현된 함수를 수행한다는 공통점이 있지만, 다음과 같은 차이점이 있다.

    • Runnable: 어떤 객체도 리턴하지 않습니다. Exception을 발생시키지 않습니다.
    • Callable: 특정 타입의 객체를 리턴합니다. Exception을 발생킬 수 있습니다.

     

    Callable

    • 이전까지 사용했던 Runnable과 유사하지만 작업의 결과(return) 를 받을 수 있다.

    Future

    • Future 는 자바 1.5 에 등장한 비동기 계산 결과를 나타내는 인터페이스
    • 비동기적인 작업의 현재 상태를 조회하거나 결과를 가져올 수 있다. 
    • Future를 이용하면 멀티쓰레드 환경에서 처리된 어떤 데이터를 다른 쓰레드에 전달할 수 있다.
    • Future 내부적으로 Thread-Safe 하도록 구현되었기 때문에 synchronized block을 사용하지 않아도 된다.
    • 비동기적인 작업을 수행?
      • 현재 진행하고 있는 Thread 가 아닌 별도의 Thread 에서 작업을 수행하는 것을 말한다.
      • 같은 Thread 에서 메서드를 호출할 때는 리턴 값을 받지만, 비동기적으로 작업을 수행할 때는 리턴 값을 전달받을 수 있는 무언가의 interface 가 필요한데 Future 가 그 역할을 한다.

     

     

    Callable 과 Future 실습하기


    결과를 가져오기 get()

    • 블록킹 콜이다.
    • 타임아웃(최대한으로 기다릴 시간)을 설정할 수 있다.

    작업 상태 확인하기 isDone()

    •  완료 했으면 true 아니면 false를 리턴한다.

    작업 취소하기 cancel()

    • 취소 했으면 true 못했으면 false를 리턴한다.
    • parameter true를 전달하면 현재 진행중인 쓰레드를 interrupt하고 그러지 않으면 현재 진행중인 작업이 끝날때까지 기다린다.
    package com.example.reactivestreamstoby;
    
    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.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    public class Main {
      public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
    
        Callable<String> hello = () -> {
          Thread.sleep(1000L);
          return "Hello";
        };
    
        System.out.println("start");
    
        // submit 시, Runnable 도 동일하게 Future 로 받을 수 있었다.
        // Callable 이 리턴하는 타입의 Future 를 받을 수 가 있게된다.
        Future<String> helloFuture = executorService.submit(hello);
    
        // 상태를 알고싶을때 상태에 따라 true/false 반환
        System.out.println(helloFuture.isDone()); 
        
        // Future 를 가지고  submit 이 만들어주는 값을 get 을 통해 꺼낼 수 있다.
        // get 이전까지는 코드가 계속해서 실행이 되지만, get 을 만나는 순간 멈춰서 결과값을 가져올때까지 기다린다. (블록킹 콜)
        helloFuture.get();
    
        // 작업 취소 기능, get() 을 할 수 없다.
        helloFuture.cancel(false);
    
        System.out.println("end");
        executorService.shutdown();
      }
    }
    

     

     

     

    여러 작업 동시에 실행하기 invokeAll()

    • 동시에 실행한 작업중에 제일 오래걸리는 작업 만큼 시간이 걸린다.
    package com.example.reactivestreamstoby;
    
    import java.util.Arrays;
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class Main {
      public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
    
        Callable<String> hello = () -> {
          Thread.sleep(1000L);
          return "hello";
        };
        Callable<String> java = () -> {
          Thread.sleep(2000L);
          return "java";
        };
        Callable<String> hyokeun = () -> {
          Thread.sleep(3000L);
          return "hyokeun";
        };
    
        // invokeAll 은 1초, 2초, 3초 로 준 애들이 다 끝날때까지 기다린다. 즉, 자바가 끝날때까지 (3초) 기다리는것
        List<Future<String>> futures = executorService.invokeAll(Arrays.asList(hello, java, hyokeun));
        for (Future<String> f : futures) {
          System.out.println(f.get());
        }
        
        executorService.shutdown();
      }
    }

     

    여러 작업 중에 하나라도 먼저 응답이 오면 끝내기 invokeAny()

    • 동시에 실행한 작업중에 제일 짧게 걸리는 작업 만큼 시간이 걸린다.
    • 블록킹 콜이다.
    package com.example.reactivestreamstoby;
    
    import java.util.Arrays;
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class Main {
      public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 싱글쓰레드로 주면 맨 처음것만 나온다. 가장 먼저 처리되고 나머지는 큐에서 대기하고 있기 때문이다.
        ExecutorService executorService = Executors.newFixedThreadPool(4);
    
        Callable<String> hello = () -> {
          Thread.sleep(5000L);
          return "hello";
        };
        Callable<String> java = () -> {
          Thread.sleep(2000L);
          return "java";
        };
        Callable<String> hyokeun = () -> {
          Thread.sleep(3000L);
          return "hyokeun";
        };
    
        // 그런데 특정 서버 3대를 두고 모두 같은 값을 가져와야한다면 3대 모두를 기다릴 필요가 있을까? -> 이 경우에 해당되는데 InvokeAny
        // 여기선 가장 빠른 java (2초) 가 출력
        String s = executorService.invokeAny(Arrays.asList(hello, java, hyokeun));
        System.out.println(s);
    
        executorService.shutdown();
      }
    }
    

     

     

     

    참고


    인프런 - 더 자바, Java8 (백기선)

Designed by Tistory.