newscheduledthreadpool - java executorservice thread pool
Elija entre la presentaciĆ³n de ExecutorService y la ejecuciĆ³n de ExecutorService (6)
Desde el execute :
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.
De modo que, dependiendo de la implementación de Executor
, puede encontrar que el subproceso se bloquea cuando la tarea se está ejecutando.
¿Cómo debo elegir entre submit o execute EjecutorService , si el valor devuelto no es de mi interés?
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());
Existe una diferencia con respecto al manejo de excepciones / errores.
Una tarea puesta en cola con execute()
que genera algo Throwable
causará que se invoque UncaughtExceptionHandler
para el Thread
ejecuta la tarea. Se UncaughtExceptionHandler
el UncaughtExceptionHandler
predeterminado, que normalmente imprime el seguimiento de pila System.err
a System.err
, si no se ha instalado ningún controlador personalizado.
Por otro lado, un Throwable
generado por una tarea en cola con submit()
vinculará el Throwable
to the Future
que se produjo a partir de la llamada a submit()
. Llamar a get()
en ese Future
arrojará una ExecutionException
con el Throwable
original como causa (accesible al llamar a getCause()
en ExecutionException
).
La respuesta completa es una composición de dos respuestas que se publicaron aquí (más un poco "extra"):
- Al enviar una tarea (en lugar de ejecutarla) obtiene un futuro que puede usarse para obtener el resultado o cancelar la acción. No tiene este tipo de control cuando
execute
(porque su tipo de retorno esvoid
) -
execute
espera unRunnable
mientras quesubmit
puede tomar unRunnable
o unCallable
como argumento (para obtener más información acerca de la diferencia entre los dos, ver a continuación). -
execute
burbujas de cualquier excepción sin marcar de inmediato (¡no puede lanzar excepciones comprobadas!), mientras quesubmit
vincula cualquier tipo de excepción al futuro que retorna como resultado, y solo cuando llama afuture.get()
a the (wrapped ) se lanzará una excepción. El Throwable que obtendrá es una instancia deExecutionException
y si llama agetCause()
este objetogetCause()
elgetCause()
original.
Algunos puntos más (relacionados):
- Incluso si la tarea que desea
submit
no requiere la devolución de un resultado, aún puede usarCallable<Void>
(en lugar de usarRunnable
). - La cancelación de tareas se puede hacer utilizando el mecanismo de interrupt . Aquí hay un ejemplo de cómo implementar una política de cancelación
En resumen, es una mejor práctica usar submit
con un Callable
(frente a execute
con Runnable
). Y citaré "concurrencia de Java en la práctica" Por Brian Goetz:
6.3.2 Tareas que producen resultados: llamadas y futuro
El marco Executor usa Runnable como su representación de tarea básica. Runnable es una abstracción bastante limitante; ejecutar no puede devolver un valor ni arrojar excepciones controladas, 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 de base de datos, búsqueda de un recurso en 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, devuelva un valor y anticipe que podría lanzar una excepción.7 Los ejecutores incluyen varios métodos de utilidad para ajustar otros tipos de tareas, incluyendo Runnable y java.security.PrivilegedAction, con un invocable.
Tomado del Javadoc:
El método de
submit
extiende el método base {@link Executor #execute
} creando y devolviendo un {@link Future} que se puede usar para cancelar la ejecución y / o esperar a que se complete.
Personalmente prefiero el uso de execute 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 de ExecutorService
, la implementación central devuelta por la llamada a Executors.newSingleThreadedExecutor()
es un ThreadPoolExecutor
.
Las llamadas de submit
son provistas por su padre AbstractExecutorService
y todas las llamadas se ejecutan internamente. ejecutar es reemplazado / provisto directamente por el ThreadPoolExecutor
.
si no te importa el tipo de devolución, utiliza execute. es lo mismo que enviar, solo sin el regreso de Future.
ejecutar : usarlo para disparar y olvidar llamadas
enviar : Úselo para inspeccionar el resultado de la llamada al método y tomar las medidas adecuadas sobre el 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)
Presenta una tarea ejecutable para su ejecución y devuelve un futuro que representa esa 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 la 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
El mismo código arroja reemplazando submit()
con execute
():
Reemplazar
service.submit(new Runnable(){
con
service.execute(new Runnable(){
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 este tipo de escenarios al usar submit ()?
- Incruste su código de tarea ( implementación ejecutable o ejecutable) con try {} catch {} block code
- 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