En annen type trådpool er "bufret". Slike trådbassenger er like ofte brukt som faste.
Som antydet av navnet, cacher denne typen trådpool tråder. Den holder ubrukte tråder i live i en begrenset tid for å gjenbruke disse trådene til å utføre nye oppgaver. Et slikt trådbasseng passer best når vi har litt rimelig lett arbeid.
Betydningen av "noen rimelig mengde" er ganske bred, men du bør vite at et slikt basseng ikke passer for alle oppgaver. Anta for eksempel at vi ønsker å lage en million oppgaver. Selv om hver enkelt tar svært liten tid, vil vi fortsatt bruke urimelig mye ressurser og redusere ytelsen. Vi bør også unngå slike puljer når utførelsestiden er uforutsigbar, for eksempel med I/O-oppgaver.
Under panseret kalles ThreadPoolExecutor- konstruktøren med følgende argumenter:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
Følgende verdier sendes til konstruktøren som argumenter:
Parameter | Verdi |
---|---|
corePoolSize (hvor mange tråder vil være klare (startet) når executor- tjenesten starter) | 0 |
maximumPoolSize (maksimalt antall tråder som en eksekveringstjeneste kan opprette) | Heltall.MAX_VALUE |
keepAliveTime (tiden som en frigjort tråd vil fortsette å leve før den blir ødelagt hvis antall tråder er større enn corePoolSize ) | 60L |
enhet (tidsenheter) | TimeUnit.SECONDS |
workQueue (implementering av en kø) | ny SynchronousQueue<Runnable>() |
Og vi kan passere vår egen implementering av ThreadFactory på nøyaktig samme måte.
La oss snakke om SynchronousQueue
Den grunnleggende ideen om en synkron overføring er ganske enkel og likevel kontraintuitiv (det vil si at intuisjon eller sunn fornuft forteller deg at det er feil): du kan legge til et element i en kø hvis og bare hvis en annen tråd mottar elementet på samme tid. Med andre ord, en synkron kø kan ikke ha oppgaver i seg, fordi så snart en ny oppgave kommer, har den utførende tråden allerede plukket opp oppgaven .
Når en ny oppgave kommer inn i køen, hvis det er en ledig aktiv tråd i bassenget, så plukker den opp oppgaven. Hvis alle trådene er opptatt, opprettes en ny tråd.
En bufret pool starter med null tråder og kan potensielt vokse til heltall.MAX_VALUE tråder. I hovedsak er størrelsen på en bufret trådpool bare begrenset av systemressurser.
For å spare systemressurser fjerner bufrede trådsamlinger tråder som er inaktive i ett minutt.
La oss se hvordan det fungerer i praksis. Vi lager en oppgaveklasse som modellerer en brukerforespørsel:
public class Task implements Runnable {
int taskNumber;
public Task(int taskNumber) {
this.taskNumber = taskNumber;
}
@Override
public void run() {
System.out.println("Processed user request #" + taskNumber + " on thread " + Thread.currentThread().getName());
}
}
I hovedmetoden lager vi newCachedThreadPool og legger deretter til 3 oppgaver for utførelse. Her skriver vi ut status for tjenesten vår (1) .
Deretter pauser vi i 30 sekunder, starter en annen oppgave og viser statusen (2) .
Etter det pauser vi hovedtråden vår i 70 sekunder, skriver ut statusen (3) , legger til 3 oppgaver igjen og skriver ut statusen igjen (4) .
På steder der vi viser status umiddelbart etter at vi har lagt til en oppgave, legger vi først til en 1-sekunds dvale for oppdatert utgang.
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 3; i++) {
executorService.submit(new Task(i));
}
TimeUnit.SECONDS.sleep(1);
System.out.println(executorService); //(1)
TimeUnit.SECONDS.sleep(30);
executorService.submit(new Task(3));
TimeUnit.SECONDS.sleep(1);
System.out.println(executorService); //(2)
TimeUnit.SECONDS.sleep(70);
System.out.println(executorService); //(3)
for (int i = 4; i < 7; i++) {
executorService.submit(new Task(i));
}
TimeUnit.SECONDS.sleep(1);
System.out.println(executorService); //(4)
executorService.shutdown();
Og her er resultatet:
Behandlet brukerforespørsel #1 på pool-1-thread-2 thread
Behandlet brukerforespørsel #2 på pool-1-thread-3 thread
(1) java.util.concurrent .ThreadPoolExecutor@f6f4d33[Kjører, bassengstørrelse = 3, aktive tråder = 0, oppgaver i kø = 0, fullførte oppgaver = 3]
Behandlet brukerforespørsel #3 på pool-1-tråd-2 tråd
(2) java.util.concurrent. ThreadPoolExecutor@f6f4d33[Kjører, bassengstørrelse = 3, aktive tråder = 0, oppgaver i kø = 0, fullførte oppgaver = 4] (3)
java.util.concurrent.ThreadPoolExecutor@f6f4d33[Kjører, bassengstørrelse = 0, aktive tråder = 0 , oppgaver i kø = 0, fullførte oppgaver = 4]
Behandlet brukerforespørsel #4 på pool-1-thread-4 thread
Behandlet brukerforespørsel #5 på pool-1-thread-5 thread
Behandlet brukerforespørsel #6 på pool-1-thread-4 thread
(4) java.util.concurrent.ThreadPoolExecutor@f6f4d33[Kjører, bassengstørrelse = 2, aktive tråder = 0, oppgaver i kø = 0, fullførte oppgaver = 7]
La oss gå over hvert av trinnene:
Steg | Forklaring |
---|---|
1 (etter 3 fullførte oppgaver) | Vi opprettet 3 tråder, og 3 oppgaver ble utført på disse tre trådene. Når status vises er alle 3 oppgavene ferdige, og trådene er klare til å utføre andre oppgaver. |
2 (etter 30 sekunders pause og utførelse av en annen oppgave) | Etter 30 sekunder med inaktivitet er trådene fortsatt i live og venter på oppgaver. En annen oppgave legges til og utføres på en tråd hentet fra bassenget av de gjenværende aktive trådene. Ingen ny tråd ble lagt til bassenget. |
3 (etter en 70-sekunders pause) | Trådene er fjernet fra bassenget. Det er ingen tråder klare til å godta oppgaver. |
4 (etter å ha utført ytterligere 3 oppgaver) | Etter at flere oppgaver ble mottatt ble det opprettet nye tråder. Denne gangen klarte bare to tråder å behandle 3 oppgaver. |
Vel, nå er du kjent med logikken til en annen type eksekutørtjeneste.
I analogi med andre metoder i verktøyklassen Executors har newCachedThreadPool -metoden også en overbelastet versjon som tar et ThreadFactory- objekt som argument.
GO TO FULL VERSION