Atunci când dezvoltăm o aplicație cu mai multe fire, trebuie să ne ocupăm de obicei de organizarea lucrărilor de fire. Cu cât aplicația noastră este mai mare și cu cât avem nevoie de mai multe fire pentru sarcini multithreaded, cu atât mai multRugabilobiectele pe care le creăm.

Trebuie remarcat aici că crearea unui fir în Java este o operațiune destul de costisitoare. Dacă creăm o instanță nouă a firului de execuție de fiecare dată pentru a efectua o operație, vom avea mari probleme cu performanța și, ca urmare, cu starea de sănătate a aplicației.

Un grup de fire și ThreadPoolExecutor ne vin în ajutor aici.

Un pool de fire este un set de fire pre-inițializate. Dimensiunea sa poate fi fixă ​​sau variabilă.

Dacă există mai multe sarcini decât fire de execuție, atunci sarcinile așteaptă într-o coadă de activități. Al N-lea thread din pool preia o sarcină din coadă, iar după ce este gata, firul preia o nouă sarcină din coadă. Odată ce toate sarcinile din coadă sunt executate, firele de execuție rămân active și așteaptă sarcini noi. Când apar sarcini noi, firele încep să le execute și ele.

ThreadPoolExecutor

Începând cu Java 5, cadrul Executor a câștigat o soluție multithreading. În general, are o mulțime de componente și scopul său este de a ne ajuta să gestionăm eficient cozile și pool-urile de fire.

Principalele interfețe sunt Executor și ExecutorService .

Executor este o interfață cu o singură metodă void execute(Runnable runnable).

Când treceți o sarcină unei implementări a acestei metode, știți că aceasta va fi executată asincron în viitor.

ExecutorService — O interfață care extinde interfața Executor , adăugând capabilități pentru executarea sarcinilor. De asemenea, are metode pentru întreruperea unei sarcini care rulează și terminarea pool-ului de fire.

ThreadPoolExecutor implementează interfețele Executor și ExecutorService și separă crearea sarcinilor de execuția sarcinii. Trebuie să implementăm obiecte Runnable și să le trimitem unui executant. ThreadPoolExecutoreste apoi responsabil cu executarea sarcinilor, crearea și lucrul cu fire .

După ce o sarcină este trimisă pentru execuție, este utilizat un fir de execuție existent în pool. Acest lucru îmbunătățește performanța. Rezolvă problema risipei de resurse la crearea și inițializarea unui fir nou și apoi din nou la colectarea gunoiului odată ce terminăm cu firul.

ThreadPoolExecutor are 4 constructori:


ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime, 
TimeUnit unit, 
BlockingQueue<Runnable> workQueue)
    

ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
    

ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime, 
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
    

ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime, 
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory, 
RejectedExecutionHandler handler)
    

Constructorul ThreadPoolExecutor are următorii parametri:

corePoolSize Acest parametru indică câte fire de execuție vor fi gata (pornite) când pornește serviciul executor.
maximumPoolSize Numărul maxim de fire de execuție pe care le poate crea un serviciu executor.
keepAliveTime Timpul în care un thread eliberat va continua să trăiască înainte de a fi distrus dacă numărul de fire este mai mare decâtcorePoolSize. Unitățile de timp sunt specificate în parametrul următor.
unitate Unități de timp (ore, minute, secunde, milisecunde etc.).
coada de lucru Implementarea unei cozi pentru sarcini.
manipulator Handler pentru sarcini care nu pot fi îndeplinite.
threadFactory Un obiect care creează fire noi la cerere. Utilizarea fabricilor de fire de execuție face ca apelurile către un nou hardware de execuție să fie independente, permițând aplicațiilor să utilizeze subclase speciale de fire, priorități și așa mai departe.

Crearea unui ThreadPoolExecutor

Clasa de utilitate Executors poate simplifica crearea unui ThreadPoolExecutor . Metodele acestei clase de utilitate ne ajută să pregătim aThreadPoolExecutorobiect.

newFixedThreadPool — Creează un pool de fire care reutiliza un număr fix de fire pentru a executa orice număr de sarcini.

ExecutorService executor = Executors.newFixedThreadPool(10);
                    
newWorkStealingPool — Creează un pool de fire în care numărul de fire este egal cu numărul de nuclee de procesor disponibile pentru JVM. Nivelul de concurență implicit este unul. Aceasta înseamnă că în pool vor fi create atâtea fire de execuție câte nuclee CPU sunt disponibile pentru JVM. Dacă nivelul de concurență este 4, atunci valoarea transmisă este utilizată în locul numărului de nuclee.

ExecutorService executor = Executors.newWorkStealingPool(4);
                    
newSingleThreadExecutor — creează un pool cu ​​un singur fir pentru a executa toate sarcinile.

ExecutorService executor = Executors.newSingleThreadExecutor();
                    
newCachedThreadPool — creează un pool de fire care creează fire noi după cum este necesar, dar reutiliza firele create anterior atunci când sunt disponibile.

ExecutorService executor = Executors.newCachedThreadPool();
                    
newScheduledThreadPool — Creează un pool de fire care poate programa comenzi pentru a fi executate după o anumită întârziere sau periodic.

ScheduledExecutorService executor = Executors.newScheduledThreadPool(10);
                    

Vom lua în considerare fiecare tip de piscină în următoarele lecții.