간단한 프로그램을 고려하십시오.
public static void main(String[] args) throws Exception {
// Create an ExecutorService with a fixed number of threads: three
ExecutorService service = Executors.newFixedThreadPool(3);
// Pass a simple Runnable task to the ExecutorService
service.submit(() -> System.out.println("done"));
}
프로그램을 실행하면 기대하는 콘솔 출력이 생성됩니다.
그러나 이것은 일반적으로 IntelliJ IDEA에서 볼 수 있는 출력이 뒤따르지 않습니다.
우리는 보통 프로그램이 끝날 때 그것을 봅니다.
왜 그런 일이 발생합니까?
newFixedThreadPool() 메서드 에 대한 설명은 ExecutorService 를 사용하여 생성된 스레드가 명시적으로 중지될 때까지 계속 존재한다고 알려줍니다 . 즉, ExecutorService 에 작업을 전달했기 때문에 작업 을 실행하기 위한 스레드가 생성되었고 작업이 완료된 후에도 해당 스레드가 계속 존재합니다.
ExecutorService에서 중지
따라서 ExecutorService 를 "종료"(또는 중지)해야 합니다 . 우리는 이것을 두 가지 방법으로 할 수 있습니다:
-
void shutdown() — 이 메서드가 호출된 후 ExecutorService는 새 작업 수락을 중지합니다. 이전에 ExecutorService 에 제출된 모든 작업은 계속 실행됩니다.
public static void main(String[] args) throws Exception { ExecutorService service = Executors.newFixedThreadPool(3); service.submit(() -> System.out.println("task 1")); service.submit(() -> System.out.println("task 2")); service.shutdown(); // A RejectedExecutionException will occur here service.submit(() -> System.out.println("task 3")); }
-
List<Runnable> shutdownNow() — 이 메서드는 현재 활성화된 작업을 중지하려고 시도합니다. 여전히 자신의 차례를 기다리고 있는 작업은 폐기되고 Runnables 목록으로 반환됩니다 .
public static void main(String[] args) throws Exception { ExecutorService service = Executors.newFixedThreadPool(5); List.of(1, 2, 3, 4, 5, 6, 7, 8).forEach(i -> service.submit(() -> System.out.println(i))); List<Runnable> runnables = service.shutdownNow(); runnables.forEach(System.out::println); }
산출:
2
4
3
java.util.concurrent.FutureTask@1e80bfe8[완료되지 않음, 작업 = java.util.concurrent.Executors$RunnableAdapter@4edde6e5[포장된 작업 = Test$$Lambda$16/0x0000000800b95040@70177ecd]]
java.util.concurrent .FutureTask@cc34f4d[완료되지 않음, 작업 = java.util.concurrent.Executors$RunnableAdapter@66a29884[포장된 작업 = Test$$Lambda$16/0x0000000800b95040@4769b07b]]
java.util.concurrent.FutureTask@6f539caf[완료되지 않음, 작업 = java.util.concurrent.Executors$RunnableAdapter@17a7cec2[Wrapped task = Test$$Lambda$16/0x0000000800b95040@65b3120a]]
5
프로세스가 종료 코드 0으로 완료됨
출력은 실행마다 다릅니다. 출력에는 두 종류의 라인이 있습니다.
-
숫자는 ExecutorService 가 해당 작업을 처리했음을 의미하며 작업을 생성하는 데 사용한 목록의 번호를 표시합니다.
-
FutureTask 객체 에서 toString() 메서드를 호출한 결과입니다 . 이러한 개체는 ExecutorService 에 제출되었지만 처리되지 않은 작업입니다 .
출력에는 또 다른 흥미로운 뉘앙스가 있습니다. 이상적인 세상에서는 표시된 모든 숫자를 먼저 본 다음 FutureTask 개체를 볼 것입니다. 그러나 동기화 문제는 출력의 라인을 뒤죽박죽으로 만듭니다.
다른 방법
ExecutorService에는 중지와 관련된 몇 가지 추가 메서드가 있습니다.
-
boolean awaitTermination(long timeout, TimeUnit unit) — 이 메서드는 자신을 호출하는 스레드를 차단합니다. 다음 세 가지 이벤트 중 하나가 발생하는 즉시 차단이 종료됩니다.
- shutdown() 메서드가 호출된 후 모든 활성 작업과 예약된 작업이 모두 실행되었습니다.
- 메서드 매개 변수에 의해 결정된 시간 제한이 경과했습니다.
- awaitTermination() 메서드 를 호출한 스레드가 종료됩니다.
이 메서드는 제한 시간이 경과하기 전에 ExecutorService가 중지된 경우 true를 반환 하고 제한 시간이 이미 경과한 경우 false를 반환 합니다.
public static void main(String[] args) throws Exception { ExecutorService service = Executors.newFixedThreadPool(2); service.submit(() -> System.out.println("task 1")); service.submit(() -> System.out.println("task 2")); service.submit(() -> System.out.println("task 3")); service.shutdown(); System.out.println(service.awaitTermination(1, TimeUnit.MICROSECONDS)); }
-
boolean isShutdown() — ExecutorService 에서 shutdown() 또는 shutdownNow() 메서드가 호출된 경우 true를 반환합니다 .
public static void main(String[] args) throws Exception { ExecutorService service = Executors.newFixedThreadPool(2); service.submit(() -> System.out.println("task 1")); service.submit(() -> System.out.println("task 2")); service.submit(() -> System.out.println("task 3")); System.out.println(service.isShutdown()); service.shutdown(); System.out.println(service.isShutdown()); }
-
boolean isTerminated() — ExecutorService 에서 shutdown () 또는 shutdownNow() 메서드가 호출되고 모든 작업이 완료되면 true를 반환합니다.
public static void main(String[] args) throws Exception { ExecutorService service = Executors.newFixedThreadPool(5); List.of(1, 2, 3, 4, 5, 6, 7, 8).forEach(i -> service.submit(() -> System.out.println(i))); service.shutdownNow(); System.out.println(service.isTerminated()); }
다음 메서드를 사용하는 예제 코드:
public static void main(String[] args) throws Exception {
ExecutorService service = Executors.newFixedThreadPool(16);
Callable<String> task = () -> {
Thread.sleep(1);
return "Done";
};
// Add 10,000 tasks to the queue
List<Future<String>> futures = IntStream.range(0, 10_000)
.mapToObj(i -> service.submit(task))
.collect(Collectors.toList());
System.out.printf("%d tasks were submitted for execution.%n", futures.size());
// Attempt to shut down
service.shutdown();
// Wait 100 milliseconds to finish the work
if (service.awaitTermination(100, TimeUnit.MILLISECONDS)) {
System.out.println("All tasks completed!");
} else {
// Stop forcibly
List<Runnable> notExecuted = service.shutdownNow();
System.out.printf("%d tasks were not started.%n", notExecuted.size());
}
System.out.printf("Total tasks completed: %d.%n", futures.stream().filter(Future::isDone).count());
}
출력(실행마다 다름):
9170 작업이 시작되지 않았습니다.
완료된 총 작업: 830개 작업.
종료 코드 0으로 프로세스가 완료되었습니다.