Quando sviluppiamo un'applicazione multi-thread, di solito dobbiamo occuparci dell'organizzazione del lavoro dei thread. Più grande è la nostra applicazione e più thread abbiamo bisogno per le attività multithread, piùEseguibileoggetti che creiamo.

Va notato qui che la creazione di un thread in Java è un'operazione piuttosto costosa. Se creiamo ogni volta una nuova istanza del thread per eseguire un'operazione, avremo grossi problemi con le prestazioni e, di conseguenza, con la salute dell'applicazione.

Un pool di thread e ThreadPoolExecutor vengono in nostro aiuto qui.

Un pool di thread è un insieme di thread pre-inizializzati. La sua dimensione può essere fissa o variabile.

Se sono presenti più attività che thread, le attività attendono in una coda di attività. L'ennesimo thread nel pool accetta un'attività dalla coda e, al termine, il thread preleva una nuova attività dalla coda. Una volta eseguite tutte le attività nella coda, i thread rimangono attivi e attendono nuove attività. Quando vengono visualizzate nuove attività, anche i thread iniziano a eseguirle.

ThreadPoolExecutor

A partire da Java 5, il framework Executor ha ottenuto una soluzione multithreading. In generale, ha molti componenti e il suo scopo è aiutarci a gestire in modo efficiente code e pool di thread.

Le interfacce principali sono Executor e ExecutorService .

Executor è un'interfaccia con un singolo metodo void execute(Runnable runnable).

Quando passi un'attività a un'implementazione di questo metodo, tieni presente che verrà eseguita in modo asincrono in futuro.

ExecutorService : un'interfaccia che estende l' interfaccia Executor , aggiungendo funzionalità per l'esecuzione di attività. Dispone inoltre di metodi per interrompere un'attività in esecuzione e terminare il pool di thread.

ThreadPoolExecutor implementa le interfacce Executor ed ExecutorService e separa la creazione dell'attività dall'esecuzione dell'attività. Dobbiamo implementare oggetti Runnable e inviarli a un esecutore. Il ThreadPoolExecutor è quindi responsabile dell'esecuzione delle attività e della creazione e dell'utilizzo dei thread.

Dopo che un'attività è stata inviata per l'esecuzione, viene utilizzato un thread esistente nel pool. Questo migliora le prestazioni. Risolve il problema dello spreco di risorse nella creazione e nell'inizializzazione di un nuovo thread, e poi di nuovo nella raccolta dei rifiuti una volta che abbiamo finito con il thread.

ThreadPoolExecutor ha 4 costruttori:


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)
    

Il costruttore ThreadPoolExecutor ha i seguenti parametri:

corePoolSize Questo parametro indica quanti thread saranno pronti (avviati) all'avvio del servizio executor.
dimensione massima della piscina Il numero massimo di thread che un servizio esecutore può creare.
keepAliveTime Il tempo in cui un thread liberato continuerà a vivere prima di essere distrutto se il numero di thread è maggiore dicorePoolSize. Le unità di tempo sono specificate nel parametro successivo.
unità Unità di tempo (ore, minuti, secondi, millisecondi, ecc.).
workQueue Implementazione di una coda per le attività.
gestore Gestore per le attività che non possono essere completate.
thread Factory Un oggetto che crea nuovi thread su richiesta. L'utilizzo di thread factory rende le chiamate a un nuovo thread indipendenti dall'hardware, consentendo alle applicazioni di utilizzare speciali sottoclassi di thread, priorità e così via.

Creazione di un ThreadPoolExecutor

La classe di utilità Executors può semplificare la creazione di un ThreadPoolExecutor . I metodi di questa classe di utilità ci aiutano a preparare aThreadPoolExecutoroggetto.

newFixedThreadPool — Crea un pool di thread che riutilizza un numero fisso di thread per eseguire un numero qualsiasi di attività.

ExecutorService executor = Executors.newFixedThreadPool(10);
                    
newWorkStealingPool — Crea un pool di thread in cui il numero di thread è uguale al numero di core del processore disponibili per la JVM. Il livello di concorrenza predefinito è uno. Ciò significa che nel pool verranno creati tanti thread quanti sono i core della CPU disponibili per la JVM. Se il livello di concorrenza è 4, viene utilizzato il valore passato anziché il numero di core.

ExecutorService executor = Executors.newWorkStealingPool(4);
                    
newSingleThreadExecutor — Crea un pool con un singolo thread per eseguire tutte le attività.

ExecutorService executor = Executors.newSingleThreadExecutor();
                    
newCachedThreadPool — Crea un pool di thread che crea nuovi thread secondo necessità, ma riutilizza i thread creati in precedenza quando sono disponibili.

ExecutorService executor = Executors.newCachedThreadPool();
                    
newScheduledThreadPool — Crea un pool di thread in grado di pianificare i comandi da eseguire dopo un determinato ritardo o periodicamente.

ScheduledExecutorService executor = Executors.newScheduledThreadPool(10);
                    

Prenderemo in considerazione ogni tipo di piscina nelle lezioni seguenti.