Een ander type threadpool is "cached". Dergelijke threadpools worden net zo vaak gebruikt als vaste.

Zoals de naam aangeeft, cachet dit soort threadpool threads. Het houdt ongebruikte threads gedurende een beperkte tijd in leven om die threads opnieuw te gebruiken om nieuwe taken uit te voeren. Zo'n draadpool is het beste voor als we redelijk wat licht werk hebben.

De betekenis van "een redelijk bedrag" is vrij breed, maar u moet weten dat zo'n pool niet geschikt is voor elk aantal taken. Stel dat we een miljoen taken willen maken. Zelfs als elk een zeer korte hoeveelheid tijd kost, zullen we nog steeds een onredelijke hoeveelheid middelen gebruiken en de prestaties verminderen. Dergelijke pools moeten we ook vermijden als de uitvoeringstijd onvoorspelbaar is, bijvoorbeeld bij I/O-taken.

Onder de motorkap wordt de ThreadPoolExecutor- constructor aangeroepen met de volgende argumenten:

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
      new SynchronousQueue<Runnable>());
}

De volgende waarden worden als argumenten doorgegeven aan de constructor:

Parameter Waarde
corePoolSize (hoeveel threads zullen klaar zijn (gestart) wanneer de executor- service start) 0
maximumPoolSize (het maximum aantal threads dat een uitvoerende service kan maken) Geheel getal.MAX_VALUE
keepAliveTime (de tijd dat een bevrijde thread blijft leven voordat hij wordt vernietigd als het aantal threads groter is dan corePoolSize ) 60L
eenheid (tijdseenheden) Tijdeenheid.SECONDEN
workQueue (implementatie van een wachtrij) nieuwe SynchronousQueue<Runnable>()

En we kunnen onze eigen implementatie van ThreadFactory op precies dezelfde manier doorgeven.

Laten we het hebben over SynchronousQueue

Het basisidee van een synchrone overdracht is vrij eenvoudig en toch contra-intuïtief (dat wil zeggen, intuïtie of gezond verstand vertelt je dat het verkeerd is): je kunt een element aan een wachtrij toevoegen als en alleen als een andere thread het element op de dezelfde tijd. Met andere woorden, een synchrone wachtrij kan geen taken bevatten, want zodra een nieuwe taak arriveert, heeft de uitvoerende thread de taak al opgepikt .

Wanneer een nieuwe taak in de wachtrij komt, als er een vrije actieve thread in de pool is, dan pakt deze de taak op. Als alle threads bezet zijn, wordt er een nieuwe thread aangemaakt.

Een pool in de cache begint met nul threads en kan mogelijk uitgroeien tot Integer.MAX_VALUE threads. In wezen wordt de grootte van een threadpool in de cache alleen beperkt door systeembronnen.

Om systeembronnen te sparen, verwijderen threadpools in de cache threads die een minuut inactief zijn.

Laten we eens kijken hoe het in de praktijk werkt. We zullen een taakklasse maken die een gebruikersverzoek modelleert:

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

In de hoofdmethode maken we newCachedThreadPool en voegen vervolgens 3 taken toe voor uitvoering. Hier printen we de status van onze service (1) .

Vervolgens pauzeren we 30 seconden, starten we een andere taak en geven we de status weer (2) .

Daarna pauzeren we onze rode draad gedurende 70 seconden, printen we de status (3) , voegen dan weer 3 taken toe, en printen weer de status (4) .

Op plekken waar we direct na het toevoegen van een taak de status weergeven, voegen we eerst 1 seconde slaap toe voor up-to-date output.

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

En hier is het resultaat:

Verwerkt gebruikersverzoek #0 op pool-1-thread-1 thread
Verwerkt gebruikersverzoek #1 op pool-1-thread-2 thread
Verwerkt gebruikersverzoek #2 op pool-1-thread-3 thread
(1) java.util.concurrent .ThreadPoolExecutor@f6f4d33[Running, poolgrootte = 3, actieve threads = 0, taken in wachtrij = 0, voltooide taken = 3]
Verwerkt gebruikersverzoek #3 op pool-1-thread-2 thread
(2) java.util.concurrent. ThreadPoolExecutor@f6f4d33[Running, poolgrootte = 3, actieve threads = 0, taken in wachtrij = 0, voltooide taken = 4] (3)
java.util.concurrent.ThreadPoolExecutor@f6f4d33[Running, poolgrootte = 0, actieve threads = 0 , taken in wachtrij = 0, voltooide taken = 4]
Verwerkt gebruikersverzoek #4 op pool-1-thread-4 thread
Verwerkt gebruikersverzoek #5 op pool-1-thread-5 thread
Verwerkt gebruikersverzoek #6 op pool-1-thread-4 thread
(4) java.util.concurrent.ThreadPoolExecutor@f6f4d33[Running, poolgrootte = 2, actieve threads = 0, taken in wachtrij = 0, voltooide taken = 7]

Laten we elk van de stappen doornemen:

Stap Uitleg
1 (na 3 voltooide taken) We hebben 3 threads gemaakt en 3 taken zijn uitgevoerd op deze drie threads.
Wanneer de status wordt weergegeven, zijn alle 3 de taken voltooid en zijn de threads klaar om andere taken uit te voeren.
2 (na 30 seconden pauze en uitvoering van een andere taak) Na 30 seconden inactiviteit zijn de threads nog steeds actief en wachten ze op taken.
Een andere taak wordt toegevoegd en uitgevoerd op een thread uit de pool van de resterende live threads.
Er is geen nieuwe thread aan de pool toegevoegd.
3 (na een pauze van 70 seconden) De draden zijn verwijderd uit het zwembad.
Er zijn geen threads klaar om taken te accepteren.
4 (na het uitvoeren van nog 3 taken) Nadat er meer taken waren ontvangen, werden nieuwe threads gemaakt. Deze keer slaagden slechts twee threads erin om 3 taken te verwerken.

Welnu, nu bent u bekend met de logica van een ander type uitvoerende service.

Naar analogie met andere methoden van de klasse Executors heeft de methode newCachedThreadPool ook een overbelaste versie die een ThreadFactory- object als argument gebruikt.