Java Language
Pool Executor, ExecutorService e Thread
Ricerca…
introduzione
L'interfaccia di Executor in Java fornisce un modo per disaccoppiare l'invio di attività dai meccanismi di esecuzione di ciascuna attività, inclusi i dettagli sull'utilizzo dei thread, la pianificazione, ecc. Normalmente viene utilizzato un Executor invece di creare esplicitamente i thread. Con gli Executor, gli sviluppatori non dovranno riscrivere in modo significativo il loro codice per essere in grado di regolare facilmente i criteri di esecuzione delle attività del loro programma.
Osservazioni
insidie
- Quando si pianifica un'attività per l'esecuzione ripetuta, a seconda di ScheduledExecutorService utilizzato, l'attività potrebbe essere sospesa da qualsiasi ulteriore esecuzione, se un'esecuzione dell'attività determina un'eccezione non gestita. Vedi Mother F ** k ScheduledExecutorService!
Fire and Forget - Runnable Tasks
Gli esecutori accettano un java.lang.Runnable
che contiene codice (potenzialmente computazionalmente o altrimenti di lunga esecuzione o pesante) da eseguire in un'altra discussione.
L'utilizzo sarebbe:
Executor exec = anExecutor;
exec.execute(new Runnable() {
@Override public void run() {
//offloaded work, no need to get result back
}
});
Si noti che con questo esecutore non si ha alcun modo per ottenere alcun valore calcolato.
Con Java 8, è possibile utilizzare lambda per abbreviare l'esempio di codice.
Executor exec = anExecutor;
exec.execute(() -> {
//offloaded work, no need to get result back
});
ThreadPoolExecutor
Un Executor comune utilizzato è il ThreadPoolExecutor
, che si occupa della gestione dei thread. È possibile configurare la quantità minima di thread che l'executor deve sempre mantenere quando non c'è molto da fare (si chiama dimensione del core) e una dimensione massima del thread a cui il Pool può crescere, se c'è più lavoro da fare. Una volta che il carico di lavoro diminuisce, il Pool riduce lentamente il numero di thread fino a quando non raggiunge la dimensione minima.
ThreadPoolExecutor pool = new ThreadPoolExecutor(
1, // keep at least one thread ready,
// even if no Runnables are executed
5, // at most five Runnables/Threads
// executed in parallel
1, TimeUnit.MINUTES, // idle Threads terminated after one
// minute, when min Pool size exceeded
new ArrayBlockingQueue<Runnable>(10)); // outstanding Runnables are kept here
pool.execute(new Runnable() {
@Override public void run() {
//code to run
}
});
Nota Se si configura ThreadPoolExecutor
con una coda illimitata , il conteggio dei thread non supererà corePoolSize
poiché i nuovi thread vengono creati solo se la coda è piena:
ThreadPoolExecutor con tutti i parametri:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler)
da JavaDoc
Se ci sono più di corePoolSize ma meno di maximumPoolSize thread in esecuzione, verrà creato un nuovo thread solo se la coda è piena.
vantaggi:
La dimensione di BlockingQueue può essere controllata e gli scenari di esaurimento della memoria possono essere evitati. Le prestazioni dell'applicazione non saranno ridotte con le dimensioni limitate della coda limitata.
È possibile utilizzare esistenti o creare nuove politiche del gestore di rifiuto.
Nel file ThreadPoolExecutor.AbortPolicy predefinito, il gestore rilascia una RejectedExecutionException di runtime in caso di rifiuto.
In
ThreadPoolExecutor.CallerRunsPolicy
, il thread che richiama execute esegue l'attività. Ciò fornisce un semplice meccanismo di controllo di feedback che rallenta la velocità con cui vengono inviate nuove attività.In
ThreadPoolExecutor.DiscardPolicy
, un'attività che non può essere eseguita viene semplicemente eliminata.In
ThreadPoolExecutor.DiscardOldestPolicy
, se l'executor non viene arrestato, l'attività in testa alla coda di lavoro viene interrotta e quindi l'esecuzione viene ritentata (che può fallire nuovamente, provocando la ripetizione).
ThreadFactory
possibile configurare CustomThreadFactory
, che è utile:- Per impostare un nome di thread più descrittivo
- Per impostare lo stato del daemon del thread
- Per impostare la priorità del thread
Ecco un esempio di come utilizzare ThreadPoolExecutor
Recupero del valore dal calcolo - Callable
Se il tuo calcolo produce un valore di ritorno che in seguito è richiesto, una semplice attività Runnable non è sufficiente. Per tali casi è possibile utilizzare ExecutorService.submit(
Callable
<T>)
che restituisce un valore al termine dell'esecuzione.
Il servizio restituirà un Future
che è possibile utilizzare per recuperare il risultato dell'esecuzione dell'attività.
// Submit a callable for execution
ExecutorService pool = anExecutorService;
Future<Integer> future = pool.submit(new Callable<Integer>() {
@Override public Integer call() {
//do some computation
return new Random().nextInt();
}
});
// ... perform other tasks while future is executed in a different thread
Quando hai bisogno di ottenere il risultato del futuro, chiama future.get()
Aspetta indefinitamente che il futuro finisca con un risultato.
try { // Blocks current thread until future is completed Integer result = future.get(); catch (InterruptedException || ExecutionException e) { // handle appropriately }
Aspetta che il futuro finisca, ma non più del tempo specificato.
try { // Blocks current thread for a maximum of 500 milliseconds. // If the future finishes before that, result is returned, // otherwise TimeoutException is thrown. Integer result = future.get(500, TimeUnit.MILLISECONDS); catch (InterruptedException || ExecutionException || TimeoutException e) { // handle appropriately }
Se il risultato di un'attività pianificata o in esecuzione non è più necessario, puoi chiamare Future.cancel(boolean)
per cancellarlo.
- Calling
cancel(false)
rimuove l'attività dalla coda delle attività da eseguire. - La chiamata
cancel(true)
interromperà anche l'attività se è attualmente in esecuzione.
Pianificazione delle attività da eseguire a un'ora fissa, dopo un ritardo o ripetutamente
La classe ScheduledExecutorService
fornisce metodi per pianificare attività singole o ripetute in diversi modi. Il seguente esempio di codice presuppone che il pool
sia stato dichiarato e inizializzato come segue:
ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
Oltre ai normali metodi ExecutorService
, l'API ScheduledExecutorService
aggiunge 4 metodi che pianificano le attività e restituiscono oggetti ScheduledFuture
. Quest'ultimo può essere utilizzato per recuperare i risultati (in alcuni casi) e annullare le attività.
Avvio di un'attività dopo un ritardo fisso
L'esempio seguente pianifica l'avvio di un'attività dopo dieci minuti.
ScheduledFuture<Integer> future = pool.schedule(new Callable<>() {
@Override public Integer call() {
// do something
return 42;
}
},
10, TimeUnit.MINUTES);
Avvio di attività a una velocità fissa
L'esempio seguente pianifica l'avvio di un'attività dopo dieci minuti, quindi ripetutamente a una velocità di una volta ogni minuto.
ScheduledFuture<?> future = pool.scheduleAtFixedRate(new Runnable() {
@Override public void run() {
// do something
}
},
10, 1, TimeUnit.MINUTES);
L'esecuzione delle attività continuerà in base alla pianificazione fino a quando il pool
verrà arrestato, il future
verrà annullato o una delle attività riscontra un'eccezione.
È garantito che le attività pianificate da una determinata chiamata scheduledAtFixedRate
non si sovrappongano nel tempo. Se un'attività richiede più tempo del periodo prescritto, la successiva e successiva esecuzione delle attività potrebbe iniziare in ritardo.
Avvio di attività con un ritardo fisso
L'esempio seguente pianifica l'avvio di un'attività dopo dieci minuti, quindi ripetutamente con un ritardo di un minuto tra la fine di un'attività e la successiva.
ScheduledFuture<?> future = pool.scheduleWithFixedDelay(new Runnable() {
@Override public void run() {
// do something
}
},
10, 1, TimeUnit.MINUTES);
L'esecuzione delle attività continuerà in base alla pianificazione fino a quando il pool
verrà arrestato, il future
verrà annullato o una delle attività riscontra un'eccezione.
Gestire l'esecuzione rifiutata
Se
- si tenta di inviare attività a un Executor di arresto o
- la coda è saturata (possibile solo con quelli delimitati) e il numero massimo di thread è stato raggiunto,
Verrà richiamato RejectedExecutionHandler.rejectedExecution(Runnable, ThreadPoolExecutor)
.
Il comportamento predefinito prevede che venga generata una eccezione RejectedExecutionException al chiamante. Ma ci sono più comportamenti predefiniti disponibili:
- ThreadPoolExecutor.AbortPolicy (predefinito, genererà REE)
- ThreadPoolExecutor.CallerRunsPolicy (esegue l'attività sul thread del chiamante - bloccandolo )
- ThreadPoolExecutor.DiscardPolicy (attività scartare silenziosamente)
- ThreadPoolExecutor.DiscardOldestPolicy ( elimina in modo silenzioso l'attività meno recente in coda e riprova l'esecuzione della nuova attività)
È possibile impostarli utilizzando uno dei costruttori ThreadPool:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) // <--
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) // <--
Puoi anche implementare il tuo comportamento estendendo l'interfaccia di RejectedExecutionHandler :
void rejectedExecution(Runnable r, ThreadPoolExecutor executor)
submit () vs execute () differenze nella gestione delle eccezioni
Generalmente il comando execute () viene usato per il fuoco e dimentica le chiamate (senza necessità di analizzare il risultato) e il comando submit () viene utilizzato per analizzare il risultato dell'oggetto Future.
Dovremmo essere consapevoli della differenza fondamentale dei meccanismi di gestione delle eccezioni tra questi due comandi.
Le eccezioni da submit () sono inghiottite dal framework se non le hai catturate.
Esempio di codice per capire la differenza:
Caso 1: invia il comando Runnable with execute (), che segnala l'eccezione.
import java.util.concurrent.*;
import java.util.*;
public class ExecuteSubmitDemo {
public ExecuteSubmitDemo() {
System.out.println("creating service");
ExecutorService service = Executors.newFixedThreadPool(2);
//ExtendedExecutor service = new ExtendedExecutor();
for (int i = 0; i < 2; i++){
service.execute(new Runnable(){
public void run(){
int a = 4, b = 0;
System.out.println("a and b=" + a + ":" + b);
System.out.println("a/b:" + (a / b));
System.out.println("Thread Name in Runnable after divide by zero:"+Thread.currentThread().getName());
}
});
}
service.shutdown();
}
public static void main(String args[]){
ExecuteSubmitDemo demo = new ExecuteSubmitDemo();
}
}
class ExtendedExecutor extends ThreadPoolExecutor {
public ExtendedExecutor() {
super(1, 1, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100));
}
// ...
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t == null && r instanceof Future<?>) {
try {
Object result = ((Future<?>) r).get();
} catch (CancellationException ce) {
t = ce;
} catch (ExecutionException ee) {
t = ee.getCause();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt(); // ignore/reset
}
}
if (t != null)
System.out.println(t);
}
}
produzione:
creating service
a and b=4:0
a and b=4:0
Exception in thread "pool-1-thread-1" Exception in thread "pool-1-thread-2" java.lang.ArithmeticException: / by zero
at ExecuteSubmitDemo$1.run(ExecuteSubmitDemo.java:15)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
java.lang.ArithmeticException: / by zero
at ExecuteSubmitDemo$1.run(ExecuteSubmitDemo.java:15)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
Caso 2: Sostituisci execute () con submit (): service.submit(new Runnable(){
In questo caso, le eccezioni sono inghiottite dal framework poiché il metodo run () non le ha catturate esplicitamente.
produzione:
creating service
a and b=4:0
a and b=4:0
Caso 3: cambia il nuovo FixedThreadPool in ExtendedExecutor
//ExecutorService service = Executors.newFixedThreadPool(2);
ExtendedExecutor service = new ExtendedExecutor();
produzione:
creating service
a and b=4:0
java.lang.ArithmeticException: / by zero
a and b=4:0
java.lang.ArithmeticException: / by zero
Ho dimostrato questo esempio per coprire due argomenti: Usa il tuo ThreadPoolExecutor personalizzato e gestisci Exectpion con ThreadPoolExecutor personalizzato.
Altra soluzione semplice al problema precedente: quando si utilizza il normale comando ExecutorService e invio, ottenere l'oggetto Future da submit () chiamata a comando get () API su Future. Cattura le tre eccezioni, che sono state citate nell'implementazione del metodo afterExecute. Vantaggio di ThreadPoolExecutor personalizzato rispetto a questo approccio: è necessario gestire il meccanismo di gestione delle eccezioni in un unico punto: ThreadPoolExecutor personalizzato.
Utilizzare i casi per diversi tipi di costrutti di concorrenza
ExecutorService executor = Executors.newFixedThreadPool(50);
È semplice e facile da usare. Nasconde i dettagli di basso livello di
ThreadPoolExecutor
.Preferisco questo quando il numero di attività
Callable/Runnable
è di numero ridotto e l'accumulo di attività nella coda illimitata non aumenta la memoria e peggiora le prestazioni del sistema. Se si dispone di vincoliCPU/Memory
, preferisco utilizzareThreadPoolExecutor
con limitazioni di capacità eRejectedExecutionHandler
per gestire il rifiuto delle attività.CountDownLatch
verrà inizializzato con un determinato conteggio. Questo conteggio viene decrementato dalle chiamate al metodocountDown()
. I thread in attesa che questo conteggio raggiunga lo zero possono chiamare uno dei metodiawait()
. Callingawait()
blocca il thread finché il conteggio non raggiunge lo zero. Questa classe abilita un thread java ad attendere fino a quando un altro set di thread completa le loro attività.Casi d'uso:
Raggiungere il parallelismo massimo: a volte vogliamo iniziare un numero di thread contemporaneamente per ottenere il massimo parallelismo
Attendi il completamento di N thread prima di iniziare l'esecuzione
Rilevamento deadlock.
ThreadPoolExecutor : fornisce più controllo. Se l'applicazione è vincolata dal numero di attività Runnable / Callable in sospeso, è possibile utilizzare la coda limitata impostando la capacità massima. Una volta che la coda raggiunge la capacità massima, è possibile definire RejectionHandler. Java fornisce quattro tipi di criteri
RejectedExecutionHandler
.ThreadPoolExecutor.AbortPolicy
, il gestore genera una runtime RejectedExecutionException in seguito a rifiuto.ThreadPoolExecutor.CallerRunsPolicy`, il thread che richiama execute esegue l'attività. Ciò fornisce un semplice meccanismo di controllo di feedback che rallenta la velocità con cui vengono inviate nuove attività.
In
ThreadPoolExecutor.DiscardPolicy
, un'attività che non può essere eseguita viene semplicemente eliminata.ThreadPoolExecutor.DiscardOldestPolicy
, se l'executor non viene arrestato, l'attività in testa alla coda di lavoro viene interrotta e quindi l'esecuzione viene ritentata (che può fallire nuovamente, provocando la ripetizione).
Se si desidera simulare il comportamento di CountDownLatch
, è possibile utilizzare il metodo invokeAll()
.
Un altro meccanismo che non hai citato è ForkJoinPool
ForkJoinPool
stato aggiunto a Java in Java 7.ForkJoinPool
è simile a JavaExecutorService
ma con una differenza.ForkJoinPool
rende facile per le attività suddividere il proprio lavoro in attività più piccole, che vengono poi inoltrate alForkJoinPool
. Il furto di attività avviene inForkJoinPool
quando i thread di lavoro liberi rubano le attività dalla coda di thread di lavoro occupata.Java 8 ha introdotto un'altra API in ExecutorService per creare pool di stealing del lavoro. Non è necessario creare
RecursiveTask
eRecursiveAction
ma è comunque possibile utilizzareForkJoinPool
.public static ExecutorService newWorkStealingPool()
Crea un pool di thread di lavoro-furto utilizzando tutti i processori disponibili come livello di parallelismo di destinazione.
Per impostazione predefinita, richiederà un numero di core CPU come parametro.
Tutti questi quattro meccanismi sono complementari l'uno all'altro. A seconda del livello di granularità che si desidera controllare, è necessario scegliere quelli giusti.
Attendere il completamento di tutte le attività in ExecutorService
Diamo un'occhiata alle varie opzioni per attendere il completamento delle attività inviate a Executor
- ExecutorService
invokeAll()
Esegue i compiti assegnati, restituendo un elenco di Futures che mantengono il loro stato e i risultati quando tutto è completato.
Esempio:
import java.util.concurrent.*;
import java.util.*;
public class InvokeAllDemo{
public InvokeAllDemo(){
System.out.println("creating service");
ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
List<MyCallable> futureList = new ArrayList<MyCallable>();
for (int i = 0; i < 10; i++){
MyCallable myCallable = new MyCallable((long)i);
futureList.add(myCallable);
}
System.out.println("Start");
try{
List<Future<Long>> futures = service.invokeAll(futureList);
} catch(Exception err){
err.printStackTrace();
}
System.out.println("Completed");
service.shutdown();
}
public static void main(String args[]){
InvokeAllDemo demo = new InvokeAllDemo();
}
class MyCallable implements Callable<Long>{
Long id = 0L;
public MyCallable(Long val){
this.id = val;
}
public Long call(){
// Add your business logic
return id;
}
}
}
Un aiuto di sincronizzazione che consente a uno o più thread di attendere fino al completamento di un insieme di operazioni eseguite in altri thread.
Un CountDownLatch viene inizializzato con un determinato conteggio. Il blocco
countDown()
i metodi fino a quando il conteggio corrente non raggiunge lo zero a causa di invocazioni del metodocountDown()
, dopo il quale tutti i thread in attesa vengono rilasciati e qualsiasi successiva chiamata di attesa attende immediatamente. Questo è un fenomeno one-shot: il conteggio non può essere resettato. Se è necessaria una versione che azzeri il conteggio, prendere in considerazione l'uso di CyclicBarrier .ForkJoinPool o
newWorkStealingPool()
in ExecutorScorrere tutti gli oggetti
Future
creati dopo l'invio aExecutorService
Modo consigliato di spegnimento dalla pagina di documentazione di Oracle di ExecutorService :
void shutdownAndAwaitTermination(ExecutorService pool) { pool.shutdown(); // Disable new tasks from being submitted try { // Wait a while for existing tasks to terminate if (!pool.awaitTermination(60, TimeUnit.SECONDS)) { pool.shutdownNow(); // Cancel currently executing tasks // Wait a while for tasks to respond to being cancelled if (!pool.awaitTermination(60, TimeUnit.SECONDS)) System.err.println("Pool did not terminate"); } } catch (InterruptedException ie) { // (Re-)Cancel if current thread also interrupted pool.shutdownNow(); // Preserve interrupt status Thread.currentThread().interrupt(); }
shutdown():
avvia uno spegnimento ordinato in cui vengono eseguite le attività precedentemente inviate, ma non verranno accettate nuove attività.shutdownNow():
tenta di interrompere tutte le attività che eseguono attivamente, interrompe l'elaborazione delle attività in attesa e restituisce un elenco delle attività che erano in attesa di esecuzione.Nell'esempio precedente, se le attività richiedono più tempo per essere completate, è possibile modificare la condizione a condizione while
Sostituire
if (!pool.awaitTermination(60, TimeUnit.SECONDS))
con
while(!pool.awaitTermination(60, TimeUnit.SECONDS)) { Thread.sleep(60000);
}
Utilizzare i casi per diversi tipi di ExecutorService
Gli esecutori restituiscono diversi tipi di ThreadPools che soddisfano esigenze specifiche.
public static ExecutorService newSingleThreadExecutor()
Crea un Executor che utilizza un singolo thread di lavoro che opera su una coda illimitata
C'è una differenza tra
newFixedThreadPool(1)
enewSingleThreadExecutor()
come dice il documento java per quest'ultimo:A differenza del newFixedThreadPool (1) altrimenti equivalente, l'executor restituito non è riconfigurabile per utilizzare thread aggiuntivi.
Ciò significa che un nuovo
newFixedThreadPool
può essere riconfigurato successivamente nel programma da:((ThreadPoolExecutor) fixedThreadPool).setMaximumPoolSize(10)
Questo non è possibile pernewSingleThreadExecutor
Casi d'uso:
- Si desidera eseguire le attività inoltrate in sequenza.
- È necessaria solo una discussione per gestire tutte le richieste
Contro:
- La coda illimitata è dannosa
public static ExecutorService newFixedThreadPool(int nThreads)
Crea un pool di thread che riutilizza un numero fisso di thread che operano su una coda non associata condivisa. In qualsiasi momento, al massimo i thread nThreads saranno attività di elaborazione attive. Se vengono inoltrate attività aggiuntive quando tutti i thread sono attivi, attenderanno in coda fino a quando un thread non sarà disponibile
Casi d'uso:
- Uso efficace dei nuclei disponibili. Configura
nThreads
comeRuntime.getRuntime().availableProcessors()
- Quando si decide che il numero di thread non deve superare un numero nel pool di thread
Contro:
- La coda illimitata è dannosa.
- Uso efficace dei nuclei disponibili. Configura
public static ExecutorService newCachedThreadPool()
Crea un pool di thread che crea nuovi thread in base alle necessità, ma riutilizzerà i thread creati in precedenza quando sono disponibili
Casi d'uso:
- Per attività asincrone di breve durata
Contro:
- La coda illimitata è dannosa.
- Ogni nuova attività creerà un nuovo thread se tutti i thread esistenti sono occupati. Se l'attività richiede una lunga durata, verrà creato più numero di thread, il che peggiorerà le prestazioni del sistema. Alternativa in questo caso:
newFixedThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
Crea un pool di thread in grado di pianificare i comandi da eseguire dopo un determinato ritardo o da eseguire periodicamente.
Casi d'uso:
- Gestione di eventi ricorrenti con ritardi, che avverranno in futuro in determinati intervalli di tempo
Contro:
- La coda illimitata è dannosa.
5.
public static ExecutorService newWorkStealingPool()
Crea un pool di thread di lavoro-furto utilizzando tutti i processori disponibili come livello di parallelismo di destinazione
Casi d'uso:
- Per dividere e conquistare il tipo di problemi.
- Uso efficace dei thread inattivi. I thread inattivi rubano le attività dai thread occupati.
Contro:
- La dimensione della coda illimitata è dannosa.
È possibile visualizzare uno svantaggio comune in tutti questi ExecutorService: coda illimitata. Questo sarà risolto con ThreadPoolExecutor
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler)
Con ThreadPoolExecutor
, è possibile
- Controlla la dimensione del pool di thread in modo dinamico
- Imposta la capacità per
BlockingQueue
- Definisci
RejectionExecutionHander
quando la coda è piena -
CustomThreadFactory
per aggiungere alcune funzionalità aggiuntive durante la creazione del thread(public Thread newThread(Runnable r)
Utilizzo di pool di thread
I pool di thread vengono utilizzati principalmente i metodi di chiamata in ExecutorService
.
I seguenti metodi possono essere utilizzati per inviare lavori per l'esecuzione:
Metodo | Descrizione |
---|---|
submit | Esegue un lavoro inviato e restituisce un futuro che può essere utilizzato per ottenere il risultato |
execute | Esegui l'attività in un momento futuro senza ottenere alcun valore di ritorno |
invokeAll | Esegui un elenco di attività e restituisci un elenco di Futures |
invokeAny | Esegue tutto ma restituisce solo il risultato di uno che ha avuto successo (senza eccezioni) |
Una volta che hai finito il Thread Pool puoi chiamare shutdown()
per chiudere il Thread Pool. Questo esegue tutte le attività in sospeso. Per attendere l'esecuzione di tutte le attività, è possibile eseguire il loop su awaitTermination
o isShutdown()
.