ExecutorService - Wachten tot discussies zijn voltooid

1. Overzicht

De ExecutorService framework maakt het gemakkelijk om taken in meerdere threads te verwerken. We gaan enkele scenario's illustreren waarin we wachten tot threads hun uitvoering hebben voltooid.

We laten ook zien hoe u een ExecutorService en wacht tot al lopende threads hun uitvoering hebben voltooid.

2. Na Beul Afsluiten

Bij gebruik van een Uitvoerder, we kunnen het afsluiten door de afsluiten() of shutdownNow () methoden. Hoewel, het zal niet wachten totdat alle threads stoppen met uitvoeren.

Wachten tot bestaande threads hun uitvoering hebben voltooid, kan worden bereikt door de wachtenTermination () methode.

Dit blokkeert de thread totdat alle taken hun uitvoering hebben voltooid of de opgegeven time-out is bereikt:

openbare leegte awaitTerminationAfterShutdown (ExecutorService threadPool) {threadPool.shutdown (); probeer {if (! threadPool.awaitTermination (60, TimeUnit.SECONDS)) {threadPool.shutdownNow (); }} catch (InterruptedException ex) {threadPool.shutdownNow (); Thread.currentThread (). Interrupt (); }}

3. Met behulp van CountDownLatch

Laten we vervolgens eens kijken naar een andere benadering om dit probleem op te lossen - met behulp van een CountDownLatch om de voltooiing van een taak aan te geven.

We kunnen het initialiseren met een waarde die het aantal keren vertegenwoordigt dat het kan worden verlaagd voordat alle threads die de wachten() methode, worden op de hoogte gebracht.

Als we bijvoorbeeld de huidige thread nodig hebben om op een andere te wachten N threads om hun uitvoering te voltooien, kunnen we de vergrendeling initialiseren met N:

ExecutorService WORKER_THREAD_POOL = Executors.newFixedThreadPool (10); CountDownLatch latch = nieuwe CountDownLatch (2); voor (int i = 0; ik {probeer {// ... latch.countDown ();} catch (InterruptedException e) {Thread.currentThread (). interrupt ();}}); } // wacht tot de vergrendeling wordt verlaagd door de twee resterende threads latch.await ();

4. Met behulp van invokeAll ()

De eerste benadering die we kunnen gebruiken om threads uit te voeren, is de invokeAll () methode. De methode retourneert een lijst met Toekomst objecten nadat alle taken zijn voltooid of de time-out is verstreken.

Ook moeten we er rekening mee houden dat de bestelling van het geretourneerde Toekomst objecten is hetzelfde als de lijst van de aangeboden Oproepbaar voorwerpen:

ExecutorService WORKER_THREAD_POOL = Executors.newFixedThreadPool (10); Lijst callables = Arrays.asList (nieuwe DelayedCallable ("snelle thread", 100), nieuwe DelayedCallable ("trage thread", 3000)); lange startProcessingTime = System.currentTimeMillis (); Lijst futures = WORKER_THREAD_POOL.invokeAll (oproepen); awaitTerminationAfterShutdown (WORKER_THREAD_POOL); long totalProcessingTime = System.currentTimeMillis () - startProcessingTime; assertTrue (totalProcessingTime> = 3000); String firstThreadResponse = futures.get (0) .get (); assertTrue ("snelle thread" .equals (firstThreadResponse)); String secondThreadResponse = futures.get (1) .get (); assertTrue ("trage thread" .equals (secondThreadResponse));

5. Met behulp van ExecutorCompletionService

Een andere benadering om meerdere threads uit te voeren, is door ExecutorCompletionService. Het maakt gebruik van een meegeleverde ExecutorService taken uitvoeren.

Een verschil voorbij invokeAll () is de volgorde waarin de Futures, die de uitgevoerde taken vertegenwoordigen, worden geretourneerd. ExecutorCompletionService gebruikt een wachtrij om de resultaten op te slaan in de volgorde waarin ze klaar zijn, terwijl invokeAll () geeft een lijst terug met dezelfde volgorde als geproduceerd door de iterator voor de gegeven takenlijst:

CompletionService-service = nieuwe ExecutorCompletionService (WORKER_THREAD_POOL); Lijst callables = Arrays.asList (nieuwe DelayedCallable ("snelle thread", 100), nieuwe DelayedCallable ("trage thread", 3000)); voor (Callable callable: callables) {service.submit (callable); } 

De resultaten zijn toegankelijk via de nemen() methode:

lange startProcessingTime = System.currentTimeMillis (); Toekomstige toekomst = service.take (); Tekenreeks firstThreadResponse = future.get (); long totalProcessingTime = System.currentTimeMillis () - startProcessingTime; assertTrue ("Eerste reactie moet van de snelle thread zijn", "snelle thread" .equals (firstThreadResponse)); assertTrue (totalProcessingTime> = 100 && totalProcessingTime = 3000 && totalProcessingTime <4000); LOG.debug ("Thread voltooid na:" + totalProcessingTime + "milliseconden"); awaitTerminationAfterShutdown (WORKER_THREAD_POOL);

6. Conclusie

Afhankelijk van het gebruik hebben we verschillende opties om te wachten tot threads hun uitvoering hebben voltooid.

EEN CountDownLatch is handig wanneer we een mechanisme nodig hebben om een ​​of meer threads te melden dat een reeks bewerkingen die door andere threads worden uitgevoerd, is voltooid.

ExecutorCompletionService is handig als we zo snel mogelijk toegang moeten hebben tot het taakresultaat en andere benaderingen als we willen wachten tot alle lopende taken zijn voltooid.

De broncode voor het artikel is beschikbaar op GitHub.