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.

Java SE 8
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:

  1. 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.

  2. È possibile utilizzare esistenti o creare nuove politiche del gestore di rifiuto.

    1. Nel file ThreadPoolExecutor.AbortPolicy predefinito, il gestore rilascia una RejectedExecutionException di runtime in caso di rifiuto.

    2. 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à.

    3. In ThreadPoolExecutor.DiscardPolicy , un'attività che non può essere eseguita viene semplicemente eliminata.

    4. 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).

  3. ThreadFactory possibile configurare Custom ThreadFactory , che è utile:

    1. Per impostare un nome di thread più descrittivo
    2. Per impostare lo stato del daemon del thread
    3. 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

  1. si tenta di inviare attività a un Executor di arresto o
  2. 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

  1. ExecutorService

    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 vincoli CPU/Memory , preferisco utilizzare ThreadPoolExecutor con limitazioni di capacità e RejectedExecutionHandler per gestire il rifiuto delle attività.

  2. CountDownLatch

    CountDownLatch verrà inizializzato con un determinato conteggio. Questo conteggio viene decrementato dalle chiamate al metodo countDown() . I thread in attesa che questo conteggio raggiunga lo zero possono chiamare uno dei metodi await() . Calling await() 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:

    1. Raggiungere il parallelismo massimo: a volte vogliamo iniziare un numero di thread contemporaneamente per ottenere il massimo parallelismo

    2. Attendi il completamento di N thread prima di iniziare l'esecuzione

    3. Rilevamento deadlock.

  1. 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 .

    1. ThreadPoolExecutor.AbortPolicy , il gestore genera una runtime RejectedExecutionException in seguito a rifiuto.

    2. 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à.

    3. In ThreadPoolExecutor.DiscardPolicy , un'attività che non può essere eseguita viene semplicemente eliminata.

    4. 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() .

  1. Un altro meccanismo che non hai citato è ForkJoinPool

    ForkJoinPool stato aggiunto a Java in Java 7. ForkJoinPool è simile a Java ExecutorService ma con una differenza. ForkJoinPool rende facile per le attività suddividere il proprio lavoro in attività più piccole, che vengono poi inoltrate al ForkJoinPool . Il furto di attività avviene in ForkJoinPool 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 e RecursiveAction ma è comunque possibile utilizzare ForkJoinPool .

    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

  1. 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;
        }
    }
}
  1. CountDownLatch

    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 metodo countDown() , 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 .

  2. ForkJoinPool o newWorkStealingPool() in Executor

  3. Scorrere tutti gli oggetti Future creati dopo l'invio a ExecutorService

  4. 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.

  1. 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) e newSingleThreadExecutor() 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 per newSingleThreadExecutor

    Casi d'uso:

    1. Si desidera eseguire le attività inoltrate in sequenza.
    2. È necessaria solo una discussione per gestire tutte le richieste

    Contro:

    1. La coda illimitata è dannosa
  2. 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:

    1. Uso efficace dei nuclei disponibili. Configura nThreads come Runtime.getRuntime().availableProcessors()
    2. Quando si decide che il numero di thread non deve superare un numero nel pool di thread

    Contro:

    1. La coda illimitata è dannosa.
  3. 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:

    1. Per attività asincrone di breve durata

    Contro:

    1. La coda illimitata è dannosa.
    2. 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
  1. 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:

    1. Gestione di eventi ricorrenti con ritardi, che avverranno in futuro in determinati intervalli di tempo

    Contro:

    1. 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:

    1. Per dividere e conquistare il tipo di problemi.
    2. Uso efficace dei thread inattivi. I thread inattivi rubano le attività dai thread occupati.

    Contro:

    1. 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

  1. Controlla la dimensione del pool di thread in modo dinamico
  2. Imposta la capacità per BlockingQueue
  3. Definisci RejectionExecutionHander quando la coda è piena
  4. 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() .



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow