Ang isa pang uri ng thread pool ay "naka-cache". Ang mga naturang thread pool ay karaniwang ginagamit tulad ng mga fixed.

Gaya ng ipinahiwatig ng pangalan, ang ganitong uri ng thread pool ay nag-cache ng mga thread. Pinapanatili nitong buhay ang mga hindi nagamit na thread sa loob ng limitadong oras upang magamit muli ang mga thread na iyon upang magsagawa ng mga bagong gawain. Ang ganitong thread pool ay pinakamainam para sa kapag mayroon kaming ilang makatwirang halaga ng magaan na trabaho.

Ang kahulugan ng "ilang makatwirang halaga" ay medyo malawak, ngunit dapat mong malaman na ang naturang pool ay hindi angkop para sa bawat bilang ng mga gawain. Halimbawa, ipagpalagay na gusto nating lumikha ng isang milyong gawain. Kahit na ang bawat isa ay tumagal ng napakaliit na oras, gagamit pa rin kami ng hindi makatwirang halaga ng mga mapagkukunan at pababain ang pagganap. Dapat din nating iwasan ang mga ganitong pool kapag hindi mahuhulaan ang oras ng pagpapatupad, halimbawa, sa mga gawaing I/O.

Sa ilalim ng hood, ang ThreadPoolExecutor constructor ay tinatawag na may mga sumusunod na argumento:

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

Ang mga sumusunod na halaga ay ipinasa sa tagabuo bilang mga argumento:

Parameter Halaga
corePoolSize (kung gaano karaming mga thread ang magiging handa (nagsisimula) kapag nagsimula ang serbisyo ng executor ) 0
maximumPoolSize (ang maximum na bilang ng mga thread na maaaring gawin ng isang executor service) Integer.MAX_VALUE
keepAliveTime (ang oras na ang isang napalayang thread ay patuloy na mabubuhay bago masira kung ang bilang ng mga thread ay mas malaki kaysa sa corePoolSize ) 60L
yunit (mga yunit ng oras) TimeUnit.SECONDS
workQueue (pagpapatupad ng isang queue) bagong SynchronousQueue<Runnable>()

At maaari naming ipasa ang aming sariling pagpapatupad ng ThreadFactory sa eksaktong parehong paraan.

Pag-usapan natin ang SynchronousQueue

Ang pangunahing ideya ng isang sabaysabay na paglilipat ay medyo simple ngunit kontra-intuitive (iyon ay, ang intuwisyon o sentido komun ay nagsasabi sa iyo na ito ay mali): maaari kang magdagdag ng isang elemento sa isang pila kung at kung ang isa pang thread ay makakatanggap ng elemento sa parehong oras. Sa madaling salita, ang isang kasabay na pila ay hindi maaaring magkaroon ng mga gawain sa loob nito, dahil sa sandaling dumating ang isang bagong gawain, kinuha na ng nagpapatupad na thread ang gawain .

Kapag ang isang bagong gawain ay pumasok sa pila, kung mayroong isang libreng aktibong thread sa pool, pagkatapos ay kinuha nito ang gawain. Kung ang lahat ng mga thread ay abala, pagkatapos ay isang bagong thread ay nilikha.

Ang isang naka-cache na pool ay nagsisimula sa zero na mga thread at maaaring maging Integer.MAX_VALUE na mga thread. Sa pangkalahatan, ang laki ng isang naka-cache na thread pool ay limitado lamang sa pamamagitan ng mga mapagkukunan ng system.

Upang makatipid ng mga mapagkukunan ng system, ang mga naka-cache na thread pool ay nag-aalis ng mga thread na idle sa loob ng isang minuto.

Tingnan natin kung paano ito gumagana sa pagsasanay. Gagawa kami ng klase ng gawain na nagmomodelo ng kahilingan ng user:

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

Sa pangunahing pamamaraan, lumikha kami ng newCachedThreadPool at pagkatapos ay magdagdag ng 3 gawain para sa pagpapatupad. Dito namin ini-print ang katayuan ng aming serbisyo (1) .

Susunod, huminto kami ng 30 segundo, magsisimula ng isa pang gawain, at ipakita ang katayuan (2) .

Pagkatapos nito, i-pause namin ang aming pangunahing thread sa loob ng 70 segundo, i-print ang status (3) , pagkatapos ay magdagdag muli ng 3 gawain, at muling i-print ang status (4) .

Sa mga lugar kung saan ipinapakita namin ang status kaagad pagkatapos magdagdag ng gawain, nagdaragdag muna kami ng 1 segundong pagtulog para sa up-to-date na 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();

At narito ang resulta:

Naprosesong kahilingan ng user #0 sa pool-1-thread-1 thread
Naprosesong kahilingan ng user #1 sa pool-1-thread-2 thread
Naprosesong kahilingan ng user #2 sa pool-1-thread-3 thread
(1) java.util.concurrent .ThreadPoolExecutor@f6f4d33[Tumatakbo, laki ng pool = 3, aktibong mga thread = 0, mga naka-queue na gawain = 0, natapos na mga gawain = 3]
Naprosesong kahilingan ng user #3 sa pool-1-thread-2 thread
(2) java.util.concurrent. ThreadPoolExecutor@f6f4d33[Tumatakbo, laki ng pool = 3, mga aktibong thread = 0, mga naka-queue na gawain = 0, mga natapos na gawain = 4] (3)
java.util.concurrent.ThreadPoolExecutor@f6f4d33[Tumatakbo, laki ng pool = 0, mga aktibong thread = 0 , mga naka-queue na gawain = 0, mga natapos na gawain = 4]
Naprosesong kahilingan ng user #4 sa pool-1-thread-4 thread
Naprosesong kahilingan ng user #5 sa pool-1-thread-5 thread
Naprosesong kahilingan ng user #6 sa pool-1-thread-4 thread
(4) java.util.concurrent.ThreadPoolExecutor@f6f4d33[Tumatakbo, laki ng pool = 2, aktibong mga thread = 0, mga naka-queue na gawain = 0, natapos na mga gawain = 7]

Balikan natin ang bawat hakbang:

Hakbang Paliwanag
1 (pagkatapos ng 3 nakumpletong gawain) Gumawa kami ng 3 thread, at 3 gawain ang naisagawa sa tatlong thread na ito.
Kapag ang katayuan ay ipinakita, ang lahat ng 3 gawain ay tapos na, at ang mga thread ay handa nang magsagawa ng iba pang mga gawain.
2 (pagkatapos ng 30 segundong paghinto at pagpapatupad ng isa pang gawain) Pagkatapos ng 30 segundo ng hindi aktibo, ang mga thread ay buhay pa rin at naghihintay para sa mga gawain.
Ang isa pang gawain ay idinagdag at isinasagawa sa isang thread na kinuha mula sa pool ng mga natitirang live na thread.
Walang bagong thread na naidagdag sa pool.
3 (pagkatapos ng 70 segundong pag-pause) Ang mga thread ay tinanggal mula sa pool.
Walang mga thread na handang tumanggap ng mga gawain.
4 (pagkatapos magsagawa ng 3 pang gawain) Matapos matanggap ang higit pang mga gawain, gumawa ng mga bagong thread. Sa pagkakataong ito, dalawang thread lang ang nakapagproseso ng 3 gawain.

Well, ngayon ay pamilyar ka sa lohika ng isa pang uri ng serbisyo ng tagapagpatupad.

Sa pamamagitan ng pagkakatulad sa iba pang mga pamamaraan ng klase ng utility ng Executors , ang newCachedThreadPool na pamamaraan ay mayroon ding overloaded na bersyon na kumukuha ng ThreadFactory object bilang argumento.