반응형

Callable

  • Runnable과 유사하지만 작업의 결과를 받을 수 있다.
    • Runnable은 리턴 값이 없기 때문에 쓰레드가 수행한 작업의 결과를 이용해서 어떠한 처리를 할 수 없다.

Future

  • 비동기적인 작업의 현재 상태를 조회하거나 결과를 가져올 수 있다.

 

결과를 가져오기 get()

ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<String> helloFuture = executorService.submit(() -> {
    Thread.sleep(2000L);
    return "Callable";
});
System.out.println("Hello");

String result = helloFuture.get();
System.out.println(result);
executorService.shutdown();
  • 블록킹 콜이다.
    • 결과 값을 가져올 때까지 해당 위치에서 대기
  • 타임아웃(최대한으로 기다릴 시간)을 설정할 수 있다.

 

작업 상태 확인하기 isDone()

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

 

작업 취소하기 cancel()

  • 취소 했으면 true 못했으면 false를 리턴한다.
  • parameter로 true를 전달하면 현재 진행중인 쓰레드를 interrupt하고 그러지 않으면 현재 진행중인 작업이 끝날때까지 기다린다.

 

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

  • 동시에 실행한 작업 중에 제일 오래 걸리는 작업 만큼 시간이 걸린다.
  • Dev와 Hello가 먼저 작업이 끝났더라도 제일 오래 걸리는 작업인 Java가 끝나야 값을 가져올 수 있다.

 

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

  • 동시에 실행한 작업 중에 제일 짧게 걸리는 작업 만큼 시간이 걸린다.
  • 제일 짧게 걸리는 Server2의 응답이 오면 값을 가져올 수 있다.
  • 블록킹 콜이다.
    • 결과 값을 가져올 때까지 해당 위치에서 대기

 

Callable, Future 예제

public class Main {
	public static void main(String[] args) {
		ExecutorService executorService = Executors.newFixedThreadPool(2);

		Callable<String> hello = () -> {
			System.out.println("Callable hello Start!");
			Thread.sleep(2000L);
			return "Hello";
		};

		Callable<String> java = () -> {
			Thread.sleep(3000L);
			return "Java";
		};
		//현재 Executor 쓰레드가 2개이기 때문에 Blocking Queue에서 대기(hello가 완료되어야 작업 수행 가능)
		Callable<String> dev = () -> {
			Thread.sleep(1000L);
			return "Dev";
		};

		/*
			invokeAll() : Callable을 뭉쳐서 여러 작업을 동시에 수행할 수 있다.
			- 작업이 제일 오래걸리는 쓰레드가 완료되어야만 값을 가져올 수 있다.
		*/
		try {
			List<Future<String>> futures = executorService.invokeAll(Arrays.asList(hello, java, dev));
			System.out.println("-------------------");
			for(Future<String> f : futures) {
				System.out.println(f.get());
			}
			System.out.println("-------------------");
		} catch (InterruptedException | ExecutionException e) {
			e.printStackTrace();
		}

		/*
			invokeAny() : Callable을 뭉쳐서 여러 작업을 동시에 수행할 수 있다.
			- 작업이 제일 빨리끝나는 쓰레드의 응답이 오면 값을 가져올 수 있다.
			- 블로킹 콜이다.
		*/
		try {
			System.out.println("-------------------");
			String s = executorService.invokeAny(Arrays.asList(hello, java, dev));
			System.out.println(s);
			System.out.println("-------------------");
		} catch (InterruptedException | ExecutionException e) {
			e.printStackTrace();
		}

		//Callable이 리턴하는 타입의 Future를 얻을 수 있다.
		Future<String> helloFuture = executorService.submit(hello);
		System.out.println("#######################");
		System.out.println(helloFuture.isDone()); //수행중인 작업이 끝났으면 true, 아니면 false
		System.out.println("Main Started!");

		/*
			현재 진행중인 작업을 interrupt 하면서 종료
		*/
		//helloFuture.cancel(true);

		/*
			현재 진행중인 작업을 기다리고 종료(==graceful)
			- 작업 완료를 기다린다고해도 cancel이 호출되면 Future에서 값을 꺼내는 것이 불가능하다.
				-> 이미 취소시킨 작업에서 꺼내려고하면 CancellationException 에러 발생
			- cancel을 하면 상태(isDone)는 무조건 true가 된다.
				-> 이 때의 true는 작업이 완료되었으니 값을 꺼낼 수 있다는 의미가 아니다.
				-> cancel 했기 때문에 종료된 것이다.
		*/
		//helloFuture.cancel(false);

		try {
			/*
				get() 이전 까지는 코드는 계속 실행된다.
				get()을 만나면 결과 값을 가져올 때까지 대기(Blocking Call)
			*/
			String s = helloFuture.get();//get()을 통해 Future의 값을 꺼낸다.
			System.out.println(helloFuture.isDone()); //작업이 끝났으면 true, 아니면 false
			System.out.println(s);
		} catch (InterruptedException | ExecutionException e) {
			e.printStackTrace();
		}
		
		System.out.println("Main End");
		executorService.shutdown();
	}
}

반응형

+ Recent posts