Vykonávatelia newCachedThreadPool () vs newFixedThreadPool ()

1. Prehľad

Pokiaľ ide o implementácie fondu vlákien, štandardná knižnica Java poskytuje veľa možností na výber. Skupiny pevných a medzipamäťových vlákien sú medzi týmito implementáciami dosť všadeprítomné.

V tomto výučbe sa dozvieme, ako fungujú fondy vlákien pod kapotou, a potom porovnáme tieto implementácie a ich prípady použitia.

2. Pool vlákien s medzipamäťou

Poďme sa pozrieť na to, ako Java vytvára oblasť volaných vlákien, keď voláme Executors.newCachedThreadPool ():

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

Skupiny vlákien s vyrovnávacou pamäťou používajú na zaradenie nových úloh do radu „synchrónne odovzdanie“. Základná myšlienka synchrónneho odovzdávania je jednoduchá a napriek tomu protiintuitívna: Položku je možné zaradiť do frontu vtedy a len vtedy, ak túto položku súčasne preberá iné vlákno. Inými slovami, the SynchronousQueue nemôže vykonávať žiadne úlohy.

Predpokladajme, že príde nová úloha. Ak vo fronte čaká nečinné vlákno, producent úlohy odovzdá úlohu danému vláknu. Inak, pretože rad je vždy plný, exekútor vytvorí nové vlákno na spracovanie tejto úlohy.

Fond v medzipamäti začína nulovými vláknami a môže potenciálne narásť Celé číslo.MAX_VALUE nite. Jediným obmedzením pre oblasť vlákien uložených v pamäti cache sú prakticky dostupné systémové prostriedky.

Kvôli lepšej správe systémových prostriedkov budú oblasti vlákien uložené v pamäti cache odstraňovať vlákna, ktoré zostanú nečinné jednu minútu.

2.1. Prípady použitia

Konfigurácia fondu vlákien uložených v pamäti cache ukladá vlákna (odtiaľ názov) na krátku dobu, aby ich bolo možné znova použiť na iné úlohy. Vo výsledku to funguje najlepšie, keď sa zaoberáme primeraným počtom krátkodobých úloh.

Kľúč je tu „primeraný“ a „krátkodobý“. Aby sme objasnili tento bod, poďme vyhodnotiť scenár, v ktorom sa fondy v pamäti cache nehodia. Tu zadáme jeden milión úloh, z ktorých každá trvá 100 mikrosekúnd:

Vyzývateľná úloha = () -> {dlhá oneHundredMicroSeconds = 100_000; dlho startedAt = System.nanoTime (); while (System.nanoTime () - startedAt task) .collect (toList ()); var výsledok = cachedPool.invokeAll (úlohy);

Týmto sa vytvorí veľa vlákien, ktoré sa premenia na neprimerané využitie pamäte, a čo je ešte horšie, veľa kontextových prepínačov CPU. Obe tieto anomálie by významne poškodili celkový výkon.

Preto by sme sa mali vyhnúť tomuto fondu vlákien, keď je čas vykonania nepredvídateľný, ako napríklad úlohy spojené s IO.

3. Fond pevných vlákien

Pozrime sa, ako fungujú bazény pevných vlákien pod kapotou:

public static ExecutorService newFixedThreadPool (int nThreads) {return new ThreadPoolExecutor (nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue ()); }

Na rozdiel od fondu vlákien uložených v medzipamäti používa tento neviazaný rad s pevným počtom vlákien, ktoré nikdy nevyprší.. Preto sa fond pevných vlákien namiesto neustále rastúceho počtu vlákien pokúša vykonávať prichádzajúce úlohy s pevným počtom vlákien. Keď sú všetky vlákna obsadené, bude exekútor zaradiť nové úlohy do frontu. Týmto spôsobom máme väčšiu kontrolu nad spotrebou zdrojov nášho programu.

Výsledkom je, že fondy pevných vlákien sú vhodnejšie pre úlohy s nepredvídateľnými časmi vykonania.

4. Nešťastné podobnosti

Doteraz sme vymenovali iba rozdiely medzi fondmi pamäte cache a pevnými vláknami.

Okrem všetkých týchto rozdielov sa obe používajú AbortPolicy ako ich politika nasýtenia. Očakávame preto, že títo exekútori urobia výnimku, keď nemôžu prijať a dokonca zaradiť ďalšie úlohy do poradia.

Pozrime sa, čo sa stane v skutočnom svete.

Skupiny vlákien s medzipamäťou budú za extrémnych okolností naďalej vytvárať ďalšie a ďalšie vlákna, takže prakticky nikdy nedosiahnu bod nasýtenia. Podobne fondy pevných vlákien budú aj naďalej pridávať ďalšie a ďalšie úlohy do svojho poradia. Preto pevné bazény tiež nikdy nedosiahnu bod nasýtenia.

Pretože oba fondy nebudú nasýtené, pri mimoriadne vysokom zaťažení budú spotrebovávať veľa pamäte na vytváranie vlákien alebo na zaradenie do frontu. Aby nedošlo k zraneniu, spoločné oblasti vlákien v medzipamäti tiež spôsobia veľa prepínačov kontextu procesora.

Každopádne do mať väčšiu kontrolu nad spotrebou zdrojov, dôrazne sa odporúča vytvoriť si vlastnú ThreadPoolExecutor:

var boundedQueue = nový ArrayBlockingQueue (1000); new ThreadPoolExecutor (10, 20, 60, SECONDS, boundedQueue, new AbortPolicy ()); 

Tu môže mať náš fond vlákien až 20 vlákien a môže zaradiť do frontu až 1000 úloh. Tiež, keď nemôže prijať ďalšie zaťaženie, jednoducho spôsobí výnimku.

5. Záver

V tomto tutoriáli sme nahliadli do zdrojového kódu JDK, aby sme zistili, aké odlišné Exekútor s práca pod kapotou. Potom sme porovnali pevné a medzipamäťové oblasti vlákien a ich prípady použitia.

Nakoniec sme sa pokúsili vyriešiť nekontrolovanú spotrebu zdrojov týchto fondov pomocou vlastných vlákien.


$config[zx-auto] not found$config[zx-overlay] not found