2010-10-14 13 views
153

Cómo debería elegir entre ExecutorService desubmit o execute, si el valor devuelto no es mi preocupación?de ExecutorService presentar y ejecutar ExecutorService de

Si pruebo ambos, no vi ninguna diferencia entre los dos excepto el valor devuelto.

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

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

Respuesta

166

Existe una diferencia con respecto al manejo de excepciones/errores.

Una tarea en cola con execute() que genera cierta Throwable hará que el UncaughtExceptionHandler para la Thread de ejecutar la tarea que se invoca. El valor predeterminado UncaughtExceptionHandler, que normalmente imprime el seguimiento de la pila Throwable en System.err, se invocará si no se ha instalado ningún controlador personalizado.

Por otro lado, un Throwable generada por una tarea en cola con submit() se unirá al Throwable a la Future que se produce a partir de la llamada a submit(). Llamar al get() en ese Future arrojará un ExecutionException con el Throwable original como su causa (accesible llamando al getCause() en el ExecutionException).

+17

Tenga en cuenta que este comportamiento no está garantizado, ya que depende de si su 'Runnable' se ajusta o no a una 'Tarea', sobre la que puede no tener control. Por ejemplo, si su 'Ejecutor' es en realidad un' ScheduledExecutorService', su tarea se envolverá internamente en un 'Future' y uncaught' Throwable's estará vinculado a este objeto. – rxg

+4

Quiero decir 'envuelto en un' Futuro 'o no', por supuesto. Consulte el Javadoc para [ScheduledThreadPoolExecutor # execute] (http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledThreadPoolExecutor.html#execute (java.lang.Runnable)), por ejemplo . – rxg

7

Tomado del Javadoc:

Método submit extiende método de base {@link Ejecutor # execute} mediante la creación y volver a {@link Future} que puede ser utilizado para cancelar la ejecución y/o esperar la finalización de .

Personalmente prefiero el uso de ejecutar porque se siente más declarativo, aunque esto realmente es una cuestión de preferencia personal.

Para obtener más información: en el caso de la implementación ExecutorService, la implementación central devuelta por la llamada al Executors.newSingleThreadedExecutor() es ThreadPoolExecutor.

Las llamadas submit son provistas por su padre AbstractExecutorService y todas las llamadas se ejecutan internamente. La ejecución es reemplazada/proporcionada por el ThreadPoolExecutor directamente.

11

si no le importa el tipo de devolución, utilice ejecutar. es lo mismo que enviar, solo sin el regreso de Future.

+9

Esto no es correcto según la respuesta aceptada. El manejo de excepciones es una diferencia bastante significativa. – Zero3

2

Desde el Javadoc:

El comando se puede ejecutar en un nuevo hilo, en un hilo combinado, o en el subproceso de llamada, según el criterio de la aplicación Ejecutor.

Dependiendo de la implementación de Executor, es posible que los bloques de subprocesos se bloqueen mientras se está ejecutando la tarea.

34

ejecutar: Se usa para activar y olvidar las llamadas

presentar: Se usa para inspeccionar el resultado de la llamada al método y tomar las acciones apropiadas sobre Future objetado devuelto por la llamada

De javadocs

submit(Callable<T> task)

Envía una tarea de devolución de valor para su ejecución y devuelve un futuro que representa los resultados pendientes de la tarea.

Future<?> submit(Runnable task)

envía una tarea Ejecutable para su ejecución y devuelve un futuro que representa que tarea.

void execute(Runnable command) 

ejecuta el comando dado en algún momento en el futuro. El comando puede ejecutarse en un nuevo hilo, en un hilo agrupado o en el hilo de llamada, a discreción de la implementación del Ejecutor.

Debe tener precaución al usar submit(). Oculta la excepción en el marco a menos que incruste su código de tarea en el bloque try{} catch{}.

Código de ejemplo: Este código se traga 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(); 
    } 
} 

salida:

java ExecuteSubmitDemo 
creating service 
a and b=4:0 

mismo código lanza mediante la sustitución de submit() con execute():

Reemplazar

service.submit(new Runnable(){ 

con

service.execute(new Runnable(){ 

de salida:

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) 

cómo manejar la este tipo de escenarios durante el uso de presentar()?

  1. Insertar el código de tareas (De cualquier Ejecutable o rescatable aplicación) con try {} catch {código de bloque}
  2. Implementar CustomThreadPoolExecutor

Nueva solución:

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); 
    } 
} 

salida:

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

Sin dudas la mejor respuesta aquí. – smeeb

+0

Buena explicación nítida. Aunque extenderlo NO es realmente necesario. Solo ese objeto futuro debe ser consumido para saber si la tarea fue exitosa o no. por lo tanto, use submit() si planea consumir Future ; de lo contrario, simplemente use execute() – prash

0

La respuesta completa es una composición de dos respuestas que fueron publicados aquí (además de un poco "extra"):

  • Al enviar una tarea (vs. ejecutándolo) obtienes un futuro que puede usarse para obtener el resultado o cancelar la acción. No es necesario que este tipo de control cuando execute (debido a que su tipo de retorno Identificación void)
  • execute espera una Runnable mientras submit puede tomar un Runnable o una Callable como un argumento (para más información sobre la diferencia entre los dos - vea abajo).
  • execute burbujas hasta cualquier-excepciones sin marcar de inmediato (no puede lanzar excepciones controladas !!!), mientras que se une submit cualquier tipo de excepción a un futuro que devuelve como resultado, y sólo cuando se llama a un future.get() la (envuelto) se lanzará una excepción. El Throwable que obtendrás es una instancia de ExecutionException y si llamas al getCause() de este objeto, devolverá el Throwable original.

Unas cuantas más (relacionados) puntos:

  • Incluso si la tarea que desea submit no requiere devolver un resultado , todavía se puede utilizar Callable<Void> (en lugar de utilizar un Runnable).
  • La cancelación de tareas se puede realizar utilizando el mecanismo interrupt. Aquí está an example de cómo poner en práctica una política de cancelación

En resumen, es una mejor práctica utilizar submit con un Callable (frente a execute con un Runnable). Y voy a citar de "concurrencia de Java en la práctica" Por Brian Goetz:

6.3.2 Resultado tareas que soportan: se puede llamar y Futuro marco

El ejecutor utiliza Ejecutable como su representación básica de tareas. Runnable es una abstracción limitante bastante ; ejecutar no puede devolver un valor o arrojar excepciones marcadas, aunque puede tener efectos secundarios, como escribir en un archivo de registro o colocar un resultado en una estructura de datos compartida.Muchas tareas son cálculos efectivamente diferidos: ejecución de una consulta en la base de datos, obtención de un recurso a través de la red , o cálculo de una función complicada. Para este tipo de tareas, Callable es una mejor abstracción: espera que el punto de entrada principal, llamada, devolverá un valor y anticipa que podría arrojar una excepción.7 Los ejecutores incluyen varios métodos de utilidad para envolver otros tipos de tareas, incluidos Runnable y java.security.PrivilegedAction, con un invocable.

Cuestiones relacionadas