Vegyünk egy egyszerű programot:


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"));
}

A program futtatása a várt konzolkimenetet adja:

Kész

De ezt nem követi az a kimenet, amelyet általában az IntelliJ IDEA-ban látunk:

A folyamat a 0 kilépési kóddal fejeződött be

Általában ezt látjuk, amikor egy program véget ér.

Miért történik ez?

A newFixedThreadPool() metódus leírása azt mondja, hogy az ExecutorService segítségével létrehozott szálak mindaddig léteznek, amíg kifejezetten le nem állítják őket. Ez azt jelenti, hogy mivel egy feladatot átadtunk az ExecutorService- nek , létrejött egy szál a végrehajtásához, és ez a szál a feladat elvégzése után is fennmarad.

Megállás az ExecutorService-nél

Ennek eredményeként "le kell állítani" (vagy le kell állítani) az ExecutorService-t . Ezt kétféleképpen tehetjük meg:

  1. void shutdown() – a metódus meghívása után az ExecutorService leállítja az új feladatok elfogadását. Az ExecutorService- nek korábban beküldött összes feladat továbbra is futni fog.

    
    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"));
    }
    
  2. List<Runnable> shutdownNow() – Ez a módszer megpróbálja leállítani az éppen aktív jobokat. Azokat a feladatokat, amelyek még mindig sorra várnak, eldobják, és a Runnables listájaként adják vissza .

    
    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);
    }
    

Kimenet:

1
2
4
3
java.util.concurrent.futuretask@1e80bfe8 [nem fejeződött be, task = java.util.concurrent.executors $runnableadapter@4edde6e5 [csomagolt feladat = teszt = $$ lambda $ 16/0x00000000B95040@70177ecd]]
Java.Urprent. .FutureTask@cc34f4d[Nincs befejezve, feladat = java.util.concurrent.Executors$RunnableAdapter@66a29884[Wrapped task = Test$$Lambda$16/0x0000000800b95040@4769b07Futilture3 f
[Nincs kész, feladat = java.util.concurrent.Executors$RunnableAdapter@17a7cec2[Wrapped task = Test$$Lambda$16/0x0000000800b95040@65b3120a]]
5

A folyamat a 0 kilépési kóddal befejeződött

A kimenet futásonként eltérő lesz. A kimenetben kétféle sor található:

  • A szám azt jelenti, hogy az ExecutorService-nek sikerült feldolgoznia a megfelelő feladatot, megjelenítve a számot a listából, amelyet a feladatok létrehozásához használtunk.

  • A toString() metódus FutureTask objektumon történő meghívásának eredményei . Ezek az objektumok azok a feladatok, amelyeket elküldtek az ExecutorService-nek , de nem kerültek feldolgozásra.

A kimenetnek van még egy érdekes árnyalata. Egy ideális világban először az összes megjelenített számot látnánk, majd a FutureTask objektumokat. A szinkronizálási problémák azonban összezavarják a sorokat a kimenetben.

Egyéb módszerek

Az ExecutorService számos további módszert kínál a leállításához:

  1. boolean awaitTermination (hosszú időtúllépés, TimeUnit egység) – ez a módszer blokkolja az őt hívó szálat. A blokk azonnal véget ér, amint az alábbi három esemény valamelyike ​​bekövetkezik:

    • a shutdown() metódus meghívása után az összes aktív job és minden ütemezett feladat végrehajtásra került;
    • a módszer paraméterei által meghatározott időkorlát letelt;
    • az awaitTermination() metódust hívó szál megszakad.

    A metódus igazat ad vissza , ha az ExecutorService leáll az időkorlát letelte előtt, és false értéket ad vissza, ha az időkorlát már lejárt.

    
    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));
    }
    
  2. boolean isShutdown()Igaz értéket ad vissza , ha a shutdown() vagy shutdownNow() metódus meghívásra került az ExecutorService- en .

    
    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());
    }
    
  3. boolean isTerminated()Igaz értéket ad vissza , ha a shutdown() vagy shutdownNow() metódus meghívásra került az ExecutorService- en , és minden feladat megtörtént.

    
    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());
    }
    

Példakód, amely ezeket a módszereket használja:


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());
}

Kimenet (futásról futtatásra különbözik):

10 000 feladatot adtak be végrehajtásra.
9170 feladat nem indult el.
Összes teljesített feladat: 830 feladat.

A folyamat a 0 kilépési kóddal fejeződött be