practice - thread java
¿Se debe preferir el Callable a Runnable? (4)
Entendí la diferencia entre Runnable
y la interfaz Callable
en Java. Desde Java 1.5 se han agregado características adicionales a la interfaz de Runnable
y se ha llamado Callable
para mantener la compatibilidad con versiones anteriores.
Mi pregunta ahora es que tenemos una interfaz Callable
, ¿siempre la usaremos? ¿Cuáles son los casos de uso de no usar Callable
y usar Runnable
?
( This es un buen artículo sobre cuáles son las diferencias entre ellos)
Ambos tienen sus usos, y ambos son compatibles con el marco Executor en java.util.concurrent. Runnable ha estado disponible por más tiempo, pero todavía está en uso y no desaconsejado.
Los Callables pueden arrojar excepciones y valores de retorno, lo que los convierte en la mejor abstracción para las tareas que generan resultados (como obtener un recurso de la red, realizar un cálculo costoso de un valor, etc.) [de Java Concurrency in Practice de Goetz, Bloch et. al., el trabajo estándar en concurrencia Java].
Entonces, si está diseñando una API, le sugiero que use Callables cuando sea posible. Si está seguro de que las tareas no devolverán valores y no arrojará excepciones, Runnables también es una opción válida. No hay blanco y negro aquí, especialmente porque Runnables se puede envolver fácilmente en Callables y viceversa.
Como nota aparte, tenga en cuenta que su implementación invocable no necesita declarar throws Exception
; el hecho de que el propio Callable declare que es solo para permitir a los implementadores lanzar cualquier excepción marcada. Sin embargo, las personas que llaman a su Callable que dependan únicamente de la interfaz de Llamadas tendrán que escribir el código de manejo de excepciones.
También tenga en cuenta que Callables no necesita devolver un valor; simplemente puede declarar su Callable para regresar Void
(con '' V
'' mayúscula).
En mi humilde opinión, Runnable es un mejor tipo para usar cuando se toma como argumento una función que
- no tiene un valor devuelto, pero solo efectos secundarios
- debe manejar excepciones y no propagarlas
No olvide que Callable.call()
arroja una excepción. Eso significa que si toma un argumento de Llamado, este Llamado puede lanzar cualquier tipo de excepción, y debe tener una forma de manejarlos todos de la manera correcta. Si no puede hacer eso, es mejor dejar que el implementador del Callable maneje la excepción como lo desea, y haga que el argumento sea Runnable para dejarlo en claro.
Prefiero Callable
s, pero en esos casos excepcionales puede necesitar que un Callable
sea Runnable
, puede implementarlo fácilmente agregando un método run()
como este a su Callable
.
public void run(){
try{
call();
} catch (Exception e){
e.printStackTrace(); // or however you log errors
}
}
En Java 8, también puede hacer una interfaz para hacer lo mismo por usted:
public interface CallableRunnable<T> extends Callable<T>, Runnable {
default void run(){
try {
call();
} catch (Exception e) {
e.printStackTrace(); // or however you log errors
}
}
}
Entonces solo tiene que cambiar todo lo que implements Callable<T>
para implements CallableRunnable<T>
esta manera, sus trabajos siempre se pueden llamar por cualquier método que requiera cualquiera. Por supuesto, si necesita un manejo de error específico, aún puede anular el método run () para manejar las excepciones lanzadas por su método call()
. Incluso podría implementar un método para hacerlo:
public interface CallableRunnable<T> extends Callable<T>, Runnable {
default void run(){
try {
call();
} catch (Exception e) {
handleCallExceptions(e);
}
}
default void handleCallExceptions(Exception e){
e.printStackTrace();
}
}
Entonces, cualquier manejo especial de excepciones solo necesita implementar su propio handleExceptions(Exception)
... pero no es necesario si no es necesario. Prefiero esto porque te permite tener una implementación que usa tu marco de trabajo, etc.
Use el caso de no usar Callable
: ScheduledExecutorService.scheduleAtFixedRate
y scheduleWithFixedDelay
solo acepta Runnable
.