En annan typ av trådpool är "cachad". Sådana trådpooler är lika vanliga som fasta.

Som framgår av namnet cacherar den här typen av trådpool trådar. Det håller oanvända trådar vid liv under en begränsad tid för att återanvända dessa trådar för att utföra nya uppgifter. En sådan trådpool passar bäst när vi har lite lagom lätt arbete.

Innebörden av "någon rimlig summa" är ganska bred, men du bör veta att en sådan pool inte är lämplig för varje antal uppgifter. Anta till exempel att vi vill skapa en miljon uppgifter. Även om var och en tar väldigt lite tid kommer vi fortfarande att använda orimligt mycket resurser och försämra prestandan. Vi bör också undvika sådana pooler när exekveringstiden är oförutsägbar, till exempel med I/O-uppgifter.

Under huven anropas ThreadPoolExecutor- konstruktorn med följande argument:

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

Följande värden skickas till konstruktorn som argument:

Parameter Värde
corePoolSize (hur många trådar kommer att vara klara (startade) när executor -tjänsten startar) 0
maximumPoolSize (det maximala antalet trådar som en exekutortjänst kan skapa) Heltal.MAX_VALUE
keepAliveTime (den tid som en frigjord tråd kommer att fortsätta leva innan den förstörs om antalet trådar är större än corePoolSize ) 60L
enhet (tidsenheter) TimeUnit.SECONDS
workQueue (implementering av en kö) ny SynchronousQueue<Runnable>()

Och vi kan skicka vår egen implementering av ThreadFactory på exakt samma sätt.

Låt oss prata om SynchronousQueue

Grundidén med en synkron överföring är ganska enkel och ändå kontraintuitiv (det vill säga intuition eller sunt förnuft säger att det är fel): du kan lägga till ett element i en kö om och bara om en annan tråd tar emot elementet vid samma tid. Med andra ord, en synkron kö kan inte ha uppgifter i sig, för så snart en ny uppgift anländer har den exekverande tråden redan plockat upp uppgiften .

När en ny uppgift kommer in i kön, om det finns en ledig aktiv tråd i poolen, plockar den upp uppgiften. Om alla trådar är upptagna skapas en ny tråd.

En cachad pool börjar med noll trådar och kan potentiellt växa till heltal.MAX_VALUE trådar. I huvudsak begränsas storleken på en cachad trådpool endast av systemresurser.

För att spara systemresurser tar cachade trådpooler bort trådar som är inaktiva i en minut.

Låt oss se hur det fungerar i praktiken. Vi skapar en uppgiftsklass som modellerar en användarförfrågan:

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 huvudmetoden skapar vi newCachedThreadPool och lägger sedan till 3 uppgifter för exekvering. Här skriver vi ut status för vår tjänst ( 1) .

Därefter pausar vi i 30 sekunder, startar en annan uppgift och visar status ( 2) .

Efter det pausar vi vår huvudtråd i 70 sekunder, skriver ut statusen (3) , lägger sedan till 3 uppgifter igen och skriver ut statusen igen (4) .

På platser där vi visar status direkt efter att vi lagt till en uppgift lägger vi först till en 1-sekunds viloläge för uppdaterad utdata.

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

Och här är resultatet:

Bearbetad användarförfrågan #0 på pool-1-thread-1-tråden
Bearbetad användarförfrågan #1 på pool-1-thread-2-tråden
Bearbetad användarförfrågan #2 på pool-1-thread-3-tråden
(1) java.util.concurrent .ThreadPoolExecutor@f6f4d33[Kör, poolstorlek = 3, aktiva trådar = 0, köade uppgifter = 0, slutförda uppgifter = 3]
Bearbetad användarförfrågan #3 på pool-1-tråd-2 tråd
(2) java.util.concurrent. ThreadPoolExecutor@f6f4d33[Kör, poolstorlek = 3, aktiva trådar = 0, köade uppgifter = 0, slutförda uppgifter = 4] (3)
java.util.concurrent.ThreadPoolExecutor@f6f4d33[Kör, poolstorlek = 0, aktiva trådar = 0 , köade uppgifter = 0, slutförda uppgifter = 4]
Bearbetad användarförfrågan #4 på pool-1-thread-4-tråden
Bearbetad användarförfrågan #5 på pool-1-thread-5-tråden
Bearbetad användarförfrågan #6 på pool-1-thread-4 tråd
(4) java.util.concurrent.ThreadPoolExecutor@f6f4d33[Kör, poolstorlek = 2, aktiva trådar = 0, köade uppgifter = 0, slutförda uppgifter = 7]

Låt oss gå över vart och ett av stegen:

Steg Förklaring
1 (efter 3 slutförda uppgifter) Vi skapade 3 trådar och 3 uppgifter utfördes på dessa tre trådar.
När statusen visas är alla tre uppgifterna gjorda och trådarna är redo att utföra andra uppgifter.
2 (efter 30 sekunders paus och utförande av en annan uppgift) Efter 30 sekunders inaktivitet är trådarna fortfarande levande och väntar på uppgifter.
En annan uppgift läggs till och exekveras på en tråd hämtad från poolen av de återstående live-trådarna.
Ingen ny tråd har lagts till i poolen.
3 (efter en paus på 70 sekunder) Trådarna har tagits bort från poolen.
Det finns inga trådar redo att acceptera uppgifter.
4 (efter att ha utfört ytterligare 3 uppgifter) Efter att fler uppgifter inkommit skapades nya trådar. Denna gång lyckades bara två trådar bearbeta 3 uppgifter.

Nåväl, nu är du bekant med logiken i en annan typ av exekutortjänst.

I analogi med andra metoder i verktygsklassen Executors har metoden newCachedThreadPool också en överbelastad version som tar ett ThreadFactory -objekt som argument.