Tänk på ett enkelt program:
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"));
}
Att köra programmet producerar den konsolutgång vi förväntar oss:
Men detta följs inte av utdata som vi vanligtvis ser i IntelliJ IDEA:
Det brukar vi se när ett program slutar.
Varför händer det?
Beskrivningen av metoden newFixedThreadPool() talar om för oss att trådar skapade med en ExecutorService fortsätter att existera tills de explicit stoppas. Det betyder att eftersom vi skickade en uppgift till ExecutorService skapades en tråd för att köra den, och den tråden fortsätter att existera även efter att uppgiften är klar.
Slutar på ExecutorService
Som ett resultat måste vi "stänga av" (eller stoppa) ExecutorService . Vi kan göra detta på två sätt:
-
void shutdown() — efter att denna metod har anropats slutar ExecutorService att acceptera nya jobb. Alla uppgifter som tidigare lämnats in till ExecutorService kommer att fortsätta att köras.
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() — Denna metod försöker stoppa jobb som för närvarande är aktiva. Uppgifter som fortfarande väntar på sin tur kasseras och returneras som en lista över körbara .
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); }
Produktion:
_
_
_
_
_ .FutureTask@cc34f4d[Ej slutförd, uppgift = java.util.concurrent.Executors$RunnableAdapter@66a29884[Omslagen uppgift = Test$$Lambda$16/0x0000000800b95040@4769b07b]
java.uFurT@4769b07b. [Ej klar, uppgift = java.util.concurrent.Executors$RunnableAdapter@17a7cec2[Wrapped task = Test$$Lambda$16/0x0000000800b95040@65b3120a]]
5
Processen avslutad med exitkod 0
Utdata kommer att skilja sig från körning till körning. Det finns 2 typer av linjer i utgången:
-
Ett nummer betyder att ExecutorService lyckades bearbeta motsvarande uppgift och visa numret från listan som vi använde för att skapa uppgifter.
-
Resultaten av att anropa metoden toString() på ett FutureTask- objekt. Dessa objekt är de uppgifter som lämnats till ExecutorService men som inte behandlats.
Utgången har en annan intressant nyans. I en idealisk värld skulle vi först se alla visade siffror, följt av FutureTask -objekten. Men synkroniseringsproblem rör ihop raderna i utgången.
Andra metoder
ExecutorService har flera fler metoder relaterade till att stoppa det:
-
boolean awaitTermination (lång timeout, TimeUnit-enhet) — denna metod blockerar tråden som anropar den. Blockeringen slutar så snart någon av följande tre händelser inträffar:
- efter att metoden shutdown() har anropats har alla aktiva jobb och alla schemalagda uppgifter utförts;
- timeouten som bestäms av metodparametrarna har förflutit;
- tråden som anropade metoden awaitTermination() avslutas.
Metoden returnerar true om ExecutorService stoppas innan timeouten löper ut, och false om timeouten redan har förflutit.
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() — Returnerar true om metoden shutdown() eller shutdownNow() har anropats på ExecutorService .
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() — Returnerar sant om metoden shutdown() eller shutdownNow() har anropats på ExecutorService och alla uppgifter är gjorda.
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()); }
Exempelkod som använder dessa metoder:
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());
}
Utdata (skillnad från körning till körning):
9170 uppgifter startades inte.
Totalt slutförda uppgifter: 830 uppgifter.
Processen avslutad med utgångskod 0
GO TO FULL VERSION