Scegli tra l'invio di ExecutorService e l'esecuzione di ExecutorService


194

Come devo scegliere tra l' invio o l' esecuzione di ExecutorService , se il valore restituito non è la mia preoccupazione?

Se provo entrambi, non vedo differenze tra i due tranne il valore restituito.

ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
threadExecutor.execute(new Task());

ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
threadExecutor.submit(new Task());

Risposte:


204

C'è una differenza per quanto riguarda la gestione di eccezioni / errori.

Un'attività in coda execute()che ne genera alcune Throwablecauserà il UncaughtExceptionHandlerrichiamo Threaddell'esecuzione dell'attività. L'impostazione predefinita UncaughtExceptionHandler, che in genere stampa la Throwabletraccia dello stack System.err, verrà richiamata se non è stato installato alcun gestore personalizzato.

D'altra parte, un Throwablegenerato da un'attività in coda con submit()si collegherà Throwablea quello Futureche è stato prodotto dalla chiamata a submit(). Richiamare get()ciò Futuregenererà ExecutionExceptionl'originale Throwablecome causa (accessibile chiamando getCause()il ExecutionException).


19
Nota che questo comportamento non è garantito in quanto dipende dal fatto che tu sia o no Runnablecoinvolto Tasko no, su cui potresti non avere alcun controllo. Ad esempio, se il tuo Executorè effettivamente un ScheduledExecutorService, il tuo compito verrà racchiuso internamente in un Futuree non catturato Throwables sarà legato a questo oggetto.
rxg,

4
Intendo "avvolto in un Futureo no", ovviamente. Vedere Javadoc per ScheduledThreadPoolExecutor # execute , ad esempio.
rxg,

61

esegui : usalo per sparare e dimenticare le chiamate

invio : utilizzarlo per ispezionare il risultato della chiamata del metodo e intraprendere le azioni appropriate sugliFutureoggetti restituiti dalla chiamata

Da javadocs

submit(Callable<T> task)

Invia un'attività che restituisce valore per l'esecuzione e restituisce un Future che rappresenta i risultati in sospeso dell'attività.

Future<?> submit(Runnable task)

Invia un'attività eseguibile per l'esecuzione e restituisce un Future che rappresenta tale attività.

void execute(Runnable command)

Esegue il comando dato in futuro. Il comando può essere eseguito in un nuovo thread, in un thread in pool o nel thread chiamante, a discrezione dell'implementazione di Executor.

Devi prendere precauzioni durante l'utilizzo submit(). Nasconde l'eccezione nel framework stesso, a meno che non si incorpori il codice attività in try{} catch{}blocco.

Codice di esempio: questo codice ingoia Arithmetic exception : / by zero.

import java.util.concurrent.*;
import java.util.*;

public class ExecuteSubmitDemo{
    public ExecuteSubmitDemo()
    {
        System.out.println("creating service");
        ExecutorService service = Executors.newFixedThreadPool(10);
        //ExtendedExecutor service = new ExtendedExecutor();
        service.submit(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();
    }
}

produzione:

java ExecuteSubmitDemo
creating service
a and b=4:0

Lo stesso codice genera sostituendo submit()con execute():

Sostituire

service.submit(new Runnable(){

con

service.execute(new Runnable(){

produzione:

java ExecuteSubmitDemo
creating service
a and b=4:0
Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero
        at ExecuteSubmitDemo$1.run(ExecuteSubmitDemo.java:14)
        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)

Come gestire questo tipo di scenari durante l'utilizzo di submit ()?

  1. Incorporare il codice attività ( esecuzione eseguibile o richiamabile) con il codice di blocco try {} catch {}
  2. Strumento CustomThreadPoolExecutor

Nuova soluzione:

import java.util.concurrent.*;
import java.util.*;

public class ExecuteSubmitDemo{
    public ExecuteSubmitDemo()
    {
        System.out.println("creating service");
        //ExecutorService service = Executors.newFixedThreadPool(10);
        ExtendedExecutor service = new ExtendedExecutor();
        service.submit(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:

java ExecuteSubmitDemo
creating service
a and b=4:0
java.lang.ArithmeticException: / by zero

Buona spiegazione nitida. Sebbene l'estensione non sia davvero necessaria. Solo quell'oggetto futuro deve essere consumato per sapere se l'attività ha avuto successo o meno. quindi, usa submit () se stai pensando di consumare Future <t> altrimenti usa semplicemente execute ()
prash

11

se non ti interessa il tipo restituito, usa execute. è lo stesso di submit, solo senza il ritorno di Future.


15
Questo non è corretto secondo la risposta accettata. La gestione delle eccezioni è una differenza piuttosto significativa.
Zero3

7

Tratto dal Javadoc:

Il metodo submitestende il metodo di base {@link Executor # execute} creando e restituendo un {@link Future} che può essere utilizzato per annullare l'esecuzione e / o attendere il completamento.

Personalmente preferisco usare execute perché sembra più dichiarativo, anche se questa è davvero una questione di preferenze personali.

Per fornire ulteriori informazioni: nel caso ExecutorServicedell'implementazione, l'implementazione principale restituita dalla chiamata Executors.newSingleThreadedExecutor()è a ThreadPoolExecutor.

Le submitchiamate sono fornite dal suo genitore AbstractExecutorServicee tutte le chiamate vengono eseguite internamente. esegui viene sovrascritto / fornito ThreadPoolExecutordirettamente da.


2

Dal Javadoc :

Il comando può essere eseguito in un nuovo thread, in un thread in pool o nel thread chiamante, a discrezione dell'implementazione di Executor.

Quindi, a seconda dell'implementazione, Executorè possibile che il thread di invio si blocchi mentre l'attività è in esecuzione.


1

La risposta completa è una composizione di due risposte che sono state pubblicate qui (più un po '"extra"):

  • Inviando un'attività (anziché eseguirla) si ottiene un futuro che può essere utilizzato per ottenere il risultato o annullare l'azione. Non hai questo tipo di controllo quando execute(perché il suo ID del tipo restituito void)
  • executesi aspetta un Runnablepo 'di tempo per submitprendere a Runnableo a Callablecome argomento (per maggiori informazioni sulla differenza tra i due - vedi sotto).
  • executeelimina immediatamente eventuali eccezioni non selezionate (non può generare eccezioni verificate !!!), mentre submitlega qualsiasi tipo di eccezione al futuro che ritorna di conseguenza e solo quando si chiama future.get()un'eccezione (incartata) verrà generata. Il Throwable che otterrai sarà un'istanza di ExecutionExceptione se chiamerai questo oggetto getCause()restituirà il Throwable originale.

Alcuni altri punti (correlati):

  • Anche se l'attività che si desidera submitnon richiede la restituzione di un risultato, è comunque possibile utilizzare Callable<Void>(anziché utilizzare a Runnable).
  • L'annullamento di attività può essere effettuato utilizzando il meccanismo di interruzione . Ecco un esempio di come implementare una politica di cancellazione

Per riassumere, è una pratica migliore da usare submitcon a Callable(vs. executecon a Runnable). E citerò da "Concorrenza Java in pratica" di Brian Goetz:

6.3.2 Compiti portatori di risultati: richiamabile e futuro

Il framework Executor utilizza Runnable come rappresentazione dell'attività di base. Runnable è un'astrazione abbastanza limitante; run non può restituire un valore o generare eccezioni verificate, sebbene possa avere effetti collaterali come la scrittura in un file di registro o l'inserimento di un risultato in una struttura di dati condivisa. Molte attività sono calcoli differiti in modo efficace: esecuzione di una query del database, recupero di una risorsa sulla rete o elaborazione di una funzione complicata. Per questi tipi di attività, Callable è un'astrazione migliore: si aspetta che il punto di ingresso principale, call, restituisca un valore e preveda che potrebbe generare un'eccezione. e java.security.PrivilegedAction, con un callable.


1

Aggiungendo semplicemente alla risposta accettata

Tuttavia, le eccezioni generate dalle attività arrivano al gestore eccezioni non rilevate solo per le attività inviate con execute (); per le attività inviate con submit () al servizio di esecuzione, qualsiasi eccezione generata viene considerata parte dello stato di restituzione dell'attività.

fonte

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.