ExecutorService - Venter på, at tråde er færdige

1. Oversigt

Det ExecutorService framework gør det let at behandle opgaver i flere tråde. Vi skal eksemplificere nogle scenarier, hvor vi venter på, at tråde afslutter deres udførelse.

Vi viser også, hvordan man elegant lukker ned en ExecutorService og vent på allerede kørende tråde for at afslutte deres udførelse.

2. Efter Eksekutorens Lukke ned

Når du bruger en Eksekutor, vi kan lukke det ved at ringe til lukke ned() eller shutdownNow () metoder. Selvom det ikke venter, indtil alle tråde holder op med at udføre.

At vente på, at eksisterende tråde afslutter deres udførelse kan opnås ved hjælp af awaitTermination () metode.

Dette blokerer tråden, indtil alle opgaver fuldfører deres udførelse, eller den angivne timeout er nået:

offentlig tomrum awaitTerminationAfterShutdown (ExecutorService threadPool) {threadPool.shutdown (); prøv {if (! threadPool.awaitTermination (60, TimeUnit.SECONDS)) {threadPool.shutdownNow (); }} fange (InterruptedException ex) {threadPool.shutdownNow (); Thread.currentThread (). Interrupt (); }}

3. Brug CountDownLatch

Lad os derefter se på en anden tilgang til løsning af dette problem - ved hjælp af en CountDownLatch for at signalere afslutningen af ​​en opgave.

Vi kan initialisere det med en værdi, der repræsenterer det antal gange, det kan mindskes før alle tråde, der har kaldt vente() metode, meddeles.

For eksempel, hvis vi har brug for den aktuelle tråd for at vente på en anden N tråde for at afslutte deres udførelse, kan vi initialisere låsen ved hjælp af N:

ExecutorService WORKER_THREAD_POOL = Executors.newFixedThreadPool (10); CountDownLatch latch = ny CountDownLatch (2); for (int i = 0; jeg {prøv {// ... latch.countDown ();} fang (InterruptedException e) {Thread.currentThread (). interrupt ();}}); } // vent på, at låsen dekrementeres af de to tilbageværende tråde latch.await ();

4. Brug påkaldeAlle ()

Den første tilgang, som vi kan bruge til at køre tråde, er påkaldeAlle () metode. Metoden returnerer en liste over Fremtid objekter, når alle opgaver er færdige, eller timeoutet udløber.

Vi skal også bemærke, at rækkefølgen af ​​den returnerede Fremtid objekter er den samme som listen over de leverede Kan kaldes genstande:

ExecutorService WORKER_THREAD_POOL = Executors.newFixedThreadPool (10); Liste callables = Arrays.asList (ny DelayedCallable ("hurtig tråd", 100), ny DelayedCallable ("langsom tråd", 3000)); lang startProcessingTime = System.currentTimeMillis (); Liste futures = WORKER_THREAD_POOL.invokeAll (callables); awaitTerminationAfterShutdown (WORKER_THREAD_POOL); lang totalProcessingTime = System.currentTimeMillis () - startProcessingTime; assertTrue (totalProcessingTime> = 3000); String firstThreadResponse = futures.get (0) .get (); assertTrue ("hurtig tråd" .equals (firstThreadResponse)); Streng secondThreadResponse = futures.get (1) .get (); assertTrue ("slow thread" .equals (secondThreadResponse));

5. Brug ExecutorCompletionService

En anden tilgang til at køre flere tråde er ved hjælp af ExecutorCompletionService. Det bruger en leveret ExecutorService at udføre opgaver.

En forskel over påkaldeAlle () er den rækkefølge, som Futures, der repræsenterer de udførte opgaver, returneres. ExecutorCompletionService bruger en kø til at gemme resultaterne i den rækkefølge, de er færdige, mens påkaldeAlle () returnerer en liste med samme rækkefølge som produceret af iteratoren for den givne opgaveliste:

CompletionService service = ny ExecutorCompletionService (WORKER_THREAD_POOL); Liste callables = Arrays.asList (ny DelayedCallable ("hurtig tråd", 100), ny DelayedCallable ("langsom tråd", 3000)); for (Callable callable: callables) {service.submit (callable); } 

Resultaterne kan tilgås ved hjælp af tage() metode:

lang startProcessingTime = System.currentTimeMillis (); Fremtidig fremtid = service.take (); String firstThreadResponse = future.get (); lang totalProcessingTime = System.currentTimeMillis () - startProcessingTime; assertTrue ("Første svar skal være fra den hurtige tråd", "hurtig tråd" .equals (firstThreadResponse)); assertTrue (totalProcessingTime> = 100 && totalProcessingTime = 3000 && totalProcessingTime <4000); LOG.debug ("Tråd færdig efter:" + totalProcessingTime + "millisekunder"); awaitTerminationAfterShutdown (WORKER_THREAD_POOL);

6. Konklusion

Afhængigt af brugssagen har vi forskellige muligheder for at vente på, at tråde afslutter deres udførelse.

EN CountDownLatch er nyttigt, når vi har brug for en mekanisme til at meddele en eller flere tråde, som et sæt operationer udført af andre tråde er afsluttet.

ExecutorCompletionService er nyttigt, når vi har brug for at få adgang til opgavens resultat så hurtigt som muligt og andre tilgange, når vi vil vente på, at alle kørende opgaver er færdige.

Kildekoden til artiklen er tilgængelig på GitHub.