Thread나 Runnable처럼 low 레벨의 api를 직접 다루는 것이 아니라 (interrupt, join, sleep 등을 사용해서 쓰레드를 만들고 관리하는 것) 이러한 작업들을 고수준의 api에게 위임하기 위해 만들어졌다. Executors가 쓰레드를 만들고 관리하기 때문에 Runnable만 제공해주면 된다.
public class ExecutorTest {
public static void main(String[] args) {
// ExecutorService 인터페이스 사용
/* ExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
executorService.submit(getRunnable("Hello "));
executorService.submit(getRunnable("Hong "));
executorService.submit(getRunnable("The "));
executorService.submit(getRunnable("Java "));
executorService.submit(getRunnable("Thread "));*/
// 다음 작업이 들어올 때까지 계속 대기하기 때문에 프로세스가 죽지 않아서 명시적으로 shutdown()을 해줘야한다.
// ScheduledExecutorService 인터페이스 사용
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
// scheduledExecutorService.schedule(getRunnable("Hello"), 3, TimeUnit.SECONDS);
scheduledExecutorService.scheduleAtFixedRate(getRunnable("Hello"), 1,2, TimeUnit.SECONDS); // 1초 기다렸다가 2초마다 출력
// scheduledExecutorService.shutdown(); // 현재 진행 중인 작업을 완료하고 종료하겠다.
// executorService.shutdownNow(); // 지금 돌고 있는 것 상관없이 지금 즉시 종료하겠다.
}
private static Runnable getRunnable(String message) {
return () -> System.out.println(message + Thread.currentThread().getName());
}
}
Runnable 인터페이스 같지만, 리턴값을 가질 수 있다. (Runnable은 반환 타입이 void)
비동기적인 작업의 현재 상태를 조회하거나 결과를 가져올 수 있게 제공해주는 인터페이스이다.
결과는 계산이 완료될 때 get()으로 가져올 수 있는데 get() 이전까지는 기다리지 않고 코드를 실행하다가 get() 하고나서 멈추고 결과값이 계산될 때까지 기다리다가 완료 후 결과를 검색한다. 이러한 방식을 블로킹 콜이라고 한다.
ExecutorService executorService = Executors.newFixedThreadPool(4);
Callable<String> hello = () -> {
Thread.sleep(2000L);
return "Hello";
};
Callable<String> java = () -> {
Thread.sleep(3000L);
return "Java";
};
Callable<String> hong = () -> {
Thread.sleep(1000L);
return "Hong";
};
List<Future<String>> futures = executorService.invokeAll(Arrays.asList(hello, java, hong));
for(Future<String> future : futures) {
System.out.println(future.get());
}
String invokeAny = executorService.invokeAny(Arrays.asList(hello, java, hong));
System.out.println(invokeAny);
자바에서 비동기 프로그래밍을 지원하는 인터페이스이다. Future에서 하기 어려웠던 작업들을 수월하게 할 수 있다.
1) 비동기로 작업 실행하기
2) 콜백 제공하기
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
System.out.println("Hello " + Thread.currentThread().getName());
return "Hello";
}).thenApply((s) -> {
System.out.println(Thread.currentThread().getName());
return s.toUpperCase();
});
System.out.println(future.get());
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
System.out.println("Hello " + Thread.currentThread().getName());
return "Hello";
}).thenAccept((s) -> {
System.out.println(Thread.currentThread().getName());
System.out.println(s.toUpperCase());
});
future.get();
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
System.out.println("Hello " + Thread.currentThread().getName());
return "Hello";
}).thenRun(() -> {
System.out.println(Thread.currentThread().getName());
});
future.get();
ExecutorService executorService = Executors.newFixedThreadPool(4);
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
System.out.println("Hello " + Thread.currentThread().getName());
return "Hello";
}, executorService).thenRunAsync(() -> {
System.out.println(Thread.currentThread().getName());
}, executorService);
future.get();
executorService.shutdown();
3) 조합하기
CompletableFuture<String> hello = CompletableFuture.supplyAsync(() -> {
System.out.println("Hello " + Thread.currentThread().getName());
return "Hello";
});
CompletableFuture<String> future = hello.thenCompose(CompletableFutureTest::getWorld);
System.out.println(future.get());
private static CompletableFuture<String> getWorld(String message) {
return CompletableFuture.supplyAsync(() -> {
System.out.println("World : " + Thread.currentThread().getName());
return message + " World";
});
}
CompletableFuture<String> hello = CompletableFuture.supplyAsync(() -> {
System.out.println("Hello " + Thread.currentThread().getName());
return "Hello";
});
CompletableFuture<String> world = CompletableFuture.supplyAsync(() -> {
System.out.println("World : " + Thread.currentThread().getName());
return "World";
});
CompletableFuture<String> future = hello.thenCombine(world, (h, w) -> h + " " + w); // BiFunction
System.out.println(future.get());
CompletableFuture<String> hello = CompletableFuture.supplyAsync(() -> {
System.out.println("Hello " + Thread.currentThread().getName());
return "Hello";
});
CompletableFuture<String> world = CompletableFuture.supplyAsync(() -> {
System.out.println("World : " + Thread.currentThread().getName());
return "World";
});
List<CompletableFuture<String>> futures = Arrays.asList(hello, world);
CompletableFuture[] futuresArray = futures.toArray(new CompletableFuture[futures.size()]);
CompletableFuture<List<String>> results = CompletableFuture.allOf(futuresArray)
.thenApply(v -> futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList()));
results.get().forEach(System.out::println);
CompletableFuture<String> hello = CompletableFuture.supplyAsync(() -> {
System.out.println("Hello " + Thread.currentThread().getName());
return "Hello";
});
CompletableFuture<String> world = CompletableFuture.supplyAsync(() -> {
System.out.println("World : " + Thread.currentThread().getName());
return "World";
});
CompletableFuture<Void> future = CompletableFuture.anyOf(hello, world).thenAccept(System.out::println);
future.get();
4) 예외처리하기
boolean throwError = true;
CompletableFuture<String> hello = CompletableFuture.supplyAsync(() -> {
if(throwError) {
throw new IllegalArgumentException();
}
System.out.println("Hello " + Thread.currentThread().getName());
return "Hello";
}).exceptionally(ex -> {
System.out.println(ex);
return "Error!";
});
System.out.println(hello.get());
boolean throwError = true;
CompletableFuture<String> hello = CompletableFuture.supplyAsync(() -> {
if(throwError) {
throw new IllegalArgumentException();
}
System.out.println("Hello " + Thread.currentThread().getName());
return "Hello";
}).handle((result, ex) -> {
if(ex != null) {
System.out.println(ex);
return "ERROR!";
}
return result;
});
System.out.println(hello.get());
*참고 자료
백기선 - 더 자바, Java 8