NewFixedThreadPool - metoden i Executors- klassen opretter en executorService med et fast antal tråde. I modsætning til newSingleThreadExecutor- metoden angiver vi, hvor mange tråde vi vil have i puljen. Under emhætten hedder følgende kode:

new ThreadPoolExecutor(nThreads, nThreads,
                                      	0L, TimeUnit.MILLISECONDS,
                                      	new LinkedBlockingQueue());

Parametrene corePoolSize (antallet af tråde, der vil være klar (startet), når executor- tjenesten starter) og maximumPoolSize (det maksimale antal tråde, som executor -tjenesten kan oprette) modtager den samme værdi - antallet af tråde, der sendes til newFixedThreadPool(nThreads ) . Og vi kan videregive vores egen implementering af ThreadFactory på nøjagtig samme måde.

Nå, lad os se, hvorfor vi har brug for sådan en ExecutorService .

Her er logikken i en ExecutorService med et fast antal (n) tråde:

  • Maksimalt n tråde vil være aktive til behandling af opgaver.
  • Hvis der indsendes mere end n opgaver, vil de blive holdt i køen, indtil tråde bliver frie.
  • Hvis en af ​​trådene fejler og afsluttes, oprettes en ny tråd til at træde i stedet.
  • Enhver tråd i poolen er aktiv, indtil poolen lukkes ned.

Forestil dig som et eksempel, at du venter på at gå gennem sikkerhedskontrollen i lufthavnen. Alle står på én linje, indtil umiddelbart før sikkerhedskontrollen er passagererne fordelt på alle de arbejdende kontrolposter. Hvis der er forsinkelse ved et af kontrolpunkterne, vil køen kun blive behandlet af det andet, indtil det første er ledigt. Og hvis et checkpoint lukker helt, så åbnes et andet checkpoint for at erstatte det, og passagerer vil fortsat blive behandlet gennem to checkpoints.

Vi bemærker med det samme, at selvom forholdene er ideelle — de lovede n tråde fungerer stabilt, og tråde, der ender med en fejl, udskiftes altid (noget som begrænsede ressourcer gør det umuligt at opnå i en rigtig lufthavn) — har systemet stadig flere ubehagelige funktioner, for der vil under ingen omstændigheder være flere tråde, selvom køen vokser hurtigere end trådene kan behandle opgaver.

Jeg foreslår at få en praktisk forståelse af, hvordan ExecutorService fungerer med et fast antal tråde. Lad os oprette en klasse, der implementerer Runnable . Objekter i denne klasse repræsenterer vores opgaver for ExecutorService .

public class Task implements Runnable {
    int taskNumber;

    public Task(int taskNumber) {
        this.taskNumber = taskNumber;
    }

    @Override
    public void run() {
try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Processed user request #" + taskNumber + " on thread " + Thread.currentThread().getName());
    }
}

I run()- metoden blokerer vi tråden i 2 sekunder, simulerer en vis arbejdsbelastning, og viser derefter den aktuelle opgaves nummer og navnet på den tråd, der udfører opgaven.

ExecutorService executorService = Executors.newFixedThreadPool(3);

        for (int i = 0; i < 30; i++) {
            executorService.execute(new Task(i));
        }

        executorService.shutdown();

Til at begynde med opretter vi i hovedmetoden en ExecutorService og sender 30 opgaver til udførelse.

Behandlet brugeranmodning #1 på pool-1-thread-2 thread
Behandlet brugeranmodning #0 på pool-1-thread-1 thread
Behandlet brugeranmodning #2 på pool-1-thread-3 thread
Behandlet brugeranmodning #5 på pool- 1-thread-3 thread
Behandlet brugeranmodning #3 på pool-1-thread-2 thread
Behandlet brugeranmodning #4 på pool-1-thread-1 thread
Behandlet brugeranmodning #8 på pool-1-thread-1 thread
Behandlet bruger anmodning #6 på pool-1-thread-3 thread
Behandlet brugeranmodning #7 på pool-1-thread-2 thread
Behandlet brugeranmodning #10 på pool-1-thread-3 thread
Behandlet brugeranmodning #9 på pool-1- thread-1 thread
Behandlet brugeranmodning #11 på pool-1-thread-2 thread
Behandlet brugeranmodning #12 på pool-1-thread-3 thread
Behandlet brugeranmodning #14 på pool-1-thread-2 thread
Behandlet brugeranmodning #13 på pool-1-thread-1 thread
Behandlet brugeranmodning #15 på pool-1-thread-3 thread
Behandlet brugeranmodning #16 på pool- 1-thread-2 thread
Behandlet brugeranmodning #17 på pool-1-thread-1 thread
Behandlet brugeranmodning #18 på pool-1-thread-3 thread
Behandlet brugeranmodning #19 på pool-1-thread-2 thread
Behandlet bruger anmodning #20 på pool-1-thread-1 tråd
Behandlet brugeranmodning #21 på pool-1-thread-3 tråd
Behandlet brugeranmodning #22 på pool-1-thread-2 tråd
Behandlet brugeranmodning #23 på pool-1- thread-1 thread
Behandlet brugeranmodning #25 på pool-1-thread-2 thread
Behandlet brugeranmodning #24 på pool-1-thread-3 thread
Behandlet brugeranmodning #26 på pool-1-thread-1 tråd
Behandlet brugeranmodning #27 på pool-1-thread-2 tråd
Behandlet brugeranmodning #28 på pool-1-thread-3 tråd
Behandlet brugeranmodning #29 på pool- 1-tråds-1 tråd

Konsoloutputtet viser os, hvordan opgaverne udføres på forskellige tråde, når de er frigivet af den forrige opgave.

Nu vil vi øge antallet af opgaver til 100, og efter at have indsendt 100 opgaver, kalder vi awaitTermination (11, SEKUNDER) metoden. Vi sender et tal og en tidsenhed som argumenter. Denne metode vil blokere hovedtråden i 11 sekunder. Så vil vi kalde shutdownNow() for at tvinge ExecutorService til at lukke ned uden at vente på, at alle opgaver er fuldført.

ExecutorService executorService = Executors.newFixedThreadPool(3);

        for (int i = 0; i < 100; i++) {
            executorService.execute(new Task(i));
        }

        executorService.awaitTermination(11, SECONDS);

        executorService.shutdownNow();
        System.out.println(executorService);

I slutningen viser vi oplysninger om tilstanden for executorService .

Her er konsoludgangen, vi får:

Behandlet brugeranmodning #0 på pool-1-thread-1 tråd
Behandlet brugeranmodning #2 på pool-1-thread-3 tråd
Behandlet brugeranmodning #1 på pool-1-thread-2 tråd
Behandlet brugeranmodning #4 på pool- 1-thread-3 thread
Behandlet brugeranmodning #5 på pool-1-thread-2 thread
Behandlet brugeranmodning #3 på pool-1-thread-1 thread
Behandlet brugeranmodning #6 på pool-1-thread-3 thread
Behandlet bruger anmodning #7 på pool-1-thread-2 thread
Behandlet brugeranmodning #8 på pool-1-thread-1 thread
Behandlet brugeranmodning #9 på pool-1-thread-3 thread
Behandlet brugeranmodning #11 på pool-1- thread-1 thread
Behandlet brugeranmodning #10 på pool-1-thread-2 thread
Behandlet brugeranmodning #13 på pool-1-thread-1 thread
Behandlet brugeranmodning #14 på pool-1-thread-2 thread
Behandlet brugeranmodning #12 på pool-1-thread-3 thread
java.util.concurrent.ThreadPoolExecutor@452b3a41[Lukker ned, poolstørrelse = 3, aktive tråde = 3 , opgaver i kø = 0, afsluttede opgaver = 15]
Behandlet brugeranmodning #17 på pool-1-tråd-3 tråd
Behandlet brugeranmodning #15 på pool-1-tråd-1 tråd
Behandlet brugeranmodning #16 på pool-1-tråd -2 tråd

Dette efterfølges af 3 InterruptedExceptions , smidt af søvnmetoder fra 3 aktive opgaver.

Vi kan se, at når programmet slutter, er 15 opgaver udført, men puljen havde stadig 3 aktive tråde, der ikke var færdige med at udføre deres opgaver. Interrupt () -metoden kaldes på disse tre tråde, hvilket betyder, at opgaven fuldføres, men i vores tilfælde kaster sleep -metoden en InterruptedException . Vi ser også, at efter at shutdownNow() metoden er kaldt, bliver opgavekøen ryddet.

Så når du bruger en ExecutorService med et fast antal tråde i puljen, skal du huske, hvordan det fungerer. Denne type er velegnet til opgaver med en kendt konstant belastning.

Her er et andet interessant spørgsmål: Hvis du skal bruge en executor til en enkelt tråd, hvilken metode skal du så kalde? newSingleThreadExecutor() eller newFixedThreadPool(1) ?

Begge bobestyrere vil have tilsvarende adfærd. Den eneste forskel er, at newSingleThreadExecutor()- metoden vil returnere en eksekvering, der ikke senere kan omkonfigureres til at bruge yderligere tråde.