Ang bagong FixedThreadPool na paraan ng klase ng Executors ay lumilikha ng isang executorService na may nakapirming bilang ng mga thread. Hindi tulad ng newSingleThreadExecutor na pamamaraan, tinutukoy namin kung gaano karaming mga thread ang gusto namin sa pool. Sa ilalim ng hood, ang sumusunod na code ay tinatawag na:

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

Ang mga parameter ng corePoolSize (ang bilang ng mga thread na magiging handa (nagsisimula) kapag nagsimula ang serbisyo ng tagapagpatupad ) at maximumPoolSize (ang maximum na bilang ng mga thread na maaaring gawin ng serbisyo ng tagapagpatupad ) — ang bilang ng mga thread na ipinasa sa newFixedThreadPool(nThreads ) . At maaari naming ipasa ang aming sariling pagpapatupad ng ThreadFactory sa eksaktong parehong paraan.

Well, tingnan natin kung bakit kailangan natin ng ganoong ExecutorService .

Narito ang lohika ng isang ExecutorService na may nakapirming numero (n) ng mga thread:

  • Isang maximum na n thread ang magiging aktibo para sa pagpoproseso ng mga gawain.
  • Kung higit sa n gawain ang isusumite, sila ay gaganapin sa pila hanggang sa maging libre ang mga thread.
  • Kung ang isa sa mga thread ay nabigo at nagwakas, isang bagong thread ang gagawin upang pumalit sa lugar nito.
  • Aktibo ang anumang thread sa pool hanggang sa isara ang pool.

Bilang halimbawa, isipin na naghihintay na dumaan sa seguridad sa paliparan. Ang bawat isa ay nakatayo sa isang linya hanggang kaagad bago ang security check, ang mga pasahero ay ibinahagi sa lahat ng gumaganang checkpoints. Kung may pagkaantala sa isa sa mga checkpoint, ang pila ay ipoproseso lamang ng pangalawa hanggang sa libre ang una. At kung ang isang checkpoint ay ganap na magsara, pagkatapos ay isa pang checkpoint ang magbubukas upang palitan ito, at ang mga pasahero ay patuloy na ipoproseso sa pamamagitan ng dalawang checkpoints.

Mapapansin namin kaagad na kahit na ang mga kundisyon ay perpekto — ang ipinangakong n mga thread ay gumagana nang matatag, at ang mga thread na nagtatapos sa isang error ay palaging pinapalitan (isang bagay na limitado ang mga mapagkukunan ay imposibleng makamit sa isang tunay na paliparan) — ang system ay mayroon pa ring ilang hindi kanais-nais na mga tampok, dahil sa anumang pagkakataon ay magkakaroon ng higit pang mga thread, kahit na ang pila ay lumalaki nang mas mabilis kaysa sa mga thread ay maaaring magproseso ng mga gawain.

Iminumungkahi kong makakuha ng praktikal na pag-unawa sa kung paano gumagana ang ExecutorService sa isang nakapirming bilang ng mga thread. Gumawa tayo ng klase na nagpapatupad ng Runnable . Ang mga bagay ng klase na ito ay kumakatawan sa aming mga gawain para sa 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());
    }
}

Sa run() na paraan, hinaharangan namin ang thread sa loob ng 2 segundo, tinutulad ang ilang workload, at pagkatapos ay ipinapakita ang numero ng kasalukuyang gawain at ang pangalan ng thread na nagpapatupad ng gawain.

ExecutorService executorService = Executors.newFixedThreadPool(3);

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

        executorService.shutdown();

Upang magsimula sa, sa pangunahing paraan, lumikha kami ng isang ExecutorService at magsumite ng 30 mga gawain para sa pagpapatupad.

Naprosesong kahilingan ng user #1 sa pool-1-thread-2 thread
Naprosesong kahilingan ng user #0 sa pool-1-thread-1 thread
Naprosesong kahilingan ng user #2 sa pool-1-thread-3 thread
Naprosesong kahilingan ng user #5 sa pool- 1-thread-3 thread
Naprosesong kahilingan ng user #3 sa pool-1-thread-2 thread
Naprosesong kahilingan ng user #4 sa pool-1-thread-1 thread
Naprosesong kahilingan ng user #8 sa pool-1-thread-1 thread
Naprosesong user kahilingan #6 sa pool-1-thread-3 thread
Naprosesong kahilingan ng user #7 sa pool-1-thread-2 thread
Naprosesong kahilingan ng user #10 sa pool-1-thread-3 thread
Naprosesong kahilingan ng user #9 sa pool-1- thread-1 thread
Naprosesong kahilingan ng user #11 sa pool-1-thread-2 thread
Naprosesong kahilingan ng user #12 sa pool-1-thread-3 thread
Naprosesong kahilingan ng user #14 sa pool-1-thread-2 thread
Naprosesong kahilingan ng user #13 sa pool-1-thread-1 thread
Naprosesong kahilingan ng user #15 sa pool-1-thread-3 thread
Naprosesong kahilingan ng user #16 sa pool- 1-thread-2 thread
Naprosesong kahilingan ng user #17 sa pool-1-thread-1 thread
Naprosesong kahilingan ng user #18 sa pool-1-thread-3 thread
Naprosesong kahilingan ng user #19 sa pool-1-thread-2 thread
Naprosesong user kahilingan #20 sa pool-1-thread-1 thread
Naprosesong kahilingan ng user #21 sa pool-1-thread-3 thread
Naprosesong kahilingan ng user #22 sa pool-1-thread-2 thread
Naprosesong kahilingan ng user #23 sa pool-1- thread-1 thread
Naprosesong kahilingan ng user #25 sa pool-1-thread-2 thread
Naprosesong kahilingan ng user #24 sa pool-1-thread-3 thread
Naprosesong kahilingan ng user #26 sa pool-1-thread-1 thread
Naprosesong kahilingan ng user #27 sa pool-1-thread-2 thread
Naprosesong kahilingan ng user #28 sa pool-1-thread-3 thread
Naprosesong kahilingan ng user #29 sa pool- 1-thread-1 thread

Ipinapakita sa amin ng output ng console kung paano isinasagawa ang mga gawain sa iba't ibang mga thread kapag nai-release na sila ng nakaraang gawain.

Ngayon, dadamihin namin ang bilang ng mga gawain sa 100, at pagkatapos magsumite ng 100 mga gawain, tatawagin namin ang awaitTermination(11, SECONDS) na paraan. Nagpapasa kami ng isang numero at yunit ng oras bilang mga argumento. Haharangan ng pamamaraang ito ang pangunahing thread sa loob ng 11 segundo. Pagkatapos ay tatawagan natin ang shutdownNow() upang pilitin ang ExecutorService na isara nang hindi naghihintay na makumpleto ang lahat ng gawain.

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

Sa dulo, magpapakita kami ng impormasyon tungkol sa estado ng executorService .

Narito ang console output na nakukuha namin:

Naprosesong kahilingan ng user #0 sa pool-1-thread-1 thread
Naprosesong kahilingan ng user #2 sa pool-1-thread-3 thread
Naprosesong kahilingan ng user #1 sa pool-1-thread-2 thread
Naprosesong kahilingan ng user #4 sa pool- 1-thread-3 thread
Naprosesong kahilingan ng user #5 sa pool-1-thread-2 thread
Naprosesong kahilingan ng user #3 sa pool-1-thread-1 thread
Naprosesong kahilingan ng user #6 sa pool-1-thread-3 thread
Naprosesong user kahilingan #7 sa pool-1-thread-2 thread
Naprosesong kahilingan ng user #8 sa pool-1-thread-1 thread
Naprosesong kahilingan ng user #9 sa pool-1-thread-3 thread
Naprosesong kahilingan ng user #11 sa pool-1- thread-1 thread
Naprosesong kahilingan ng user #10 sa pool-1-thread-2 thread
Naprosesong kahilingan ng user #13 sa pool-1-thread-1 thread
Naprosesong kahilingan ng user #14 sa thread ng pool-1-thread-2
Naprosesong kahilingan ng user #12 sa thread na pool-1-thread-3
java.util.concurrent.ThreadPoolExecutor@452b3a41[Pag-shut down, laki ng pool = 3, aktibong thread = 3 , mga nakapila na gawain = 0, nakumpletong mga gawain = 15]
Naprosesong kahilingan ng user #17 sa pool-1-thread-3 thread
Naprosesong kahilingan ng user #15 sa pool-1-thread-1 thread
Naprosesong kahilingan ng user #16 sa pool-1-thread -2 thread

Sinusundan ito ng 3 InterruptedExceptions , na itinapon ng mga paraan ng pagtulog mula sa 3 aktibong gawain.

Makikita natin na kapag natapos ang programa, 15 na gawain ang tapos na, ngunit ang pool ay mayroon pa ring 3 aktibong thread na hindi natapos na isagawa ang kanilang mga gawain. Ang interrupt() na pamamaraan ay tinatawag sa tatlong mga thread na ito, na nangangahulugan na ang gawain ay makumpleto, ngunit sa aming kaso, ang paraan ng pagtulog ay nagtatapon ng isang InterruptedException . Nakikita rin natin na pagkatapos tawagin ang pamamaraan ng shutdownNow() , ang pila ng gawain ay na-clear.

Kaya kapag gumagamit ng ExecutorService na may nakapirming bilang ng mga thread sa pool, siguraduhing tandaan kung paano ito gumagana. Ang ganitong uri ay angkop para sa mga gawain na may kilalang pare-pareho ang pagkarga.

Narito ang isa pang kawili-wiling tanong: kung kailangan mong gumamit ng isang tagapagpatupad para sa isang solong thread, aling paraan ang dapat mong tawagan? newSingleThreadExecutor() o newFixedThreadPool(1) ?

Ang parehong mga tagapagpatupad ay magkakaroon ng katumbas na pag-uugali. Ang kaibahan lang ay ang newSingleThreadExecutor() na paraan ay magbabalik ng executor na hindi maaaring muling i-configure sa ibang pagkakataon upang gumamit ng mga karagdagang thread.