Respuestas:
Ver explicación aquí .
La interfaz invocable es similar a Runnable, ya que ambas están diseñadas para clases cuyas instancias son potencialmente ejecutadas por otro subproceso. Un Runnable, sin embargo, no devuelve un resultado y no puede lanzar una excepción marcada.
¿Cuáles son las diferencias en las aplicaciones de
Runnable
yCallable
. ¿La diferencia solo está presente con el parámetro de retornoCallable
?
Básicamente sí. Ver las respuestas a esta pregunta . Y el javadoc paraCallable
.
¿Cuál es la necesidad de tener ambos si
Callable
puede hacer todo lo queRunnable
hace?
¡Porque la Runnable
interfaz no puede hacer todo lo que Callable
hace!
Runnable
ha existido desde Java 1.0, pero Callable
solo se introdujo en Java 1.5 ... para manejar casos de uso que Runnable
no son compatibles. En teoría, el equipo de Java podría haber cambiado la firma del Runnable.run()
método, pero esto habría roto la compatibilidad binaria con el código anterior a 1.5, lo que requeriría la codificación al migrar el código de Java antiguo a las JVM más nuevas. Eso es un GRAN NO-NO. Java se esfuerza por ser compatible con versiones anteriores ... y ese ha sido uno de los mayores puntos de venta de Java para la informática empresarial.
Y, obviamente, hay casos de uso donde una tarea no necesita devolver un resultado o lanzar una excepción marcada. Para esos casos de uso, usar Runnable
es más conciso que usar Callable<Void>
y devolver un null
valor ficticio ( ) del call()
método.
Runnable
existe (en gran parte) por razones de compatibilidad con versiones anteriores. Pero, ¿no hay situaciones en las que sea innecesario o demasiado costoso implementar (o requerir) la Callable
interfaz (por ejemplo, en ScheduledFuture<?> ScheduledExecutorService.schedule(Runnable command, long delay, TimeUnit unit)
)? Entonces, ¿no hay un beneficio en mantener ambas interfaces en el idioma, incluso si la historia no forzó el resultado actual?
Runnable
se habría modificado si no hubiera habido un imperativo para mantener la compatibilidad. El "repetitivo" de return null;
es un argumento débil. (Al menos, esa habría sido mi decisión ... en el contexto hipotético en el que podría ignorar la compatibilidad con versiones anteriores.)
Callable
necesita implementar el call()
método mientras que Runnable
necesita implementar el run()
método.Callable
puede devolver un valor pero a Runnable
no puede.Callable
puede lanzar una excepción marcada pero Runnable
no puede.A Callable
se puede usar con ExecutorService#invokeXXX(Collection<? extends Callable<T>> tasks)
métodos pero a Runnable
no se puede.
public interface Runnable {
void run();
}
public interface Callable<V> {
V call() throws Exception;
}
Encontré esto en otro blog que puede explicar un poco más estas diferencias :
Aunque ambas interfaces son implementadas por las clases que desean ejecutar en un hilo de ejecución diferente, pero hay algunas diferencias entre las dos interfaces que son:
Callable<V>
instancia devuelve un resultado de tipo V
, mientras que una Runnable
instancia no.Callable<V>
instancia puede arrojar excepciones marcadas, mientras que una Runnable
instancia no puedeLos diseñadores de Java sintieron la necesidad de ampliar las capacidades de la Runnable
interfaz, pero no querían afectar los usos de la Runnable
interfaz y probablemente esa fue la razón por la que optaron por tener una interfaz separada nombrada Callable
en Java 1.5 en lugar de cambiar existente Runnable
.
Veamos dónde se usarían Runnable y Callable.
Runnable y Callable se ejecutan en un subproceso diferente que el subproceso de llamada. Pero Callable puede devolver un valor y Runnable no. Entonces, ¿dónde se aplica esto realmente?
Runnable : si tiene un incendio y olvida la tarea, use Runnable. Coloque su código dentro de un Runnable y cuando se llame al método run (), puede realizar su tarea. El hilo de llamada realmente no le importa cuando realiza su tarea.
Invocable : si está intentando recuperar un valor de una tarea, use Invocable. Ahora invocable por sí solo no hará el trabajo. Necesitará un futuro que envuelva su invocable y obtenga sus valores en future.get (). Aquí el hilo de llamada se bloqueará hasta que el futuro regrese con resultados que a su vez están esperando que se ejecute el método call () de Callable.
Así que piense en una interfaz para una clase de destino donde tenga definidos los métodos ajustables Runnable y Callable. La clase que realiza la llamada llamará aleatoriamente a sus métodos de interfaz sin saber cuál es Runnable y cuál es invocable. Los métodos Runnable se ejecutarán de forma asíncrona, hasta que se llame a un método invocable. Aquí el hilo de la clase que llama se bloqueará ya que está recuperando valores de su clase objetivo.
NOTA: Dentro de su clase de destino, puede realizar llamadas a Callable y Runnable en un único ejecutor de subprocesos, lo que hace que este mecanismo sea similar a una cola de despacho en serie. Por lo tanto, mientras la persona que llama llame a sus métodos envueltos Runnable, el hilo de llamada se ejecutará realmente rápido sin bloqueo. Tan pronto como llame a un método invocable envuelto en el futuro, tendrá que bloquear hasta que se ejecuten todos los demás elementos en cola. Solo entonces el método volverá con valores. Este es un mecanismo de sincronización.
Callable
La interfaz declara el call()
método y debe proporcionar los genéricos, ya que el tipo de Object call () debería devolver:
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
Runnable
Por otro lado, hay una interfaz que declara el run()
método al que se llama cuando se crea un subproceso con el ejecutable y se llama a start (). También puede llamar directamente a run () pero eso solo ejecuta el método run () es el mismo hilo.
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
Para resumir algunas diferencias notables son
Runnable
objeto no devuelve un resultado, mientras que un Callable
objeto devuelve un resultado.Runnable
objeto no puede lanzar una excepción marcada cuando un Callable
objeto puede lanzar una excepción.Runnable
interfaz ha existido desde Java 1.0, mientras que Callable
solo se introdujo en Java 1.5.Pocas similitudes incluyen
Los métodos en la interfaz ExecutorService son
<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
<T> Future<T> submit(Runnable task, T result);
Propósito de estas interfaces de la documentación de Oracle:
La interfaz ejecutable debe ser implementada por cualquier clase cuyas instancias estén destinadas a ser ejecutadas por a Thread
. La clase debe definir un método sin argumentos invocados run
.
Llamable : una tarea que devuelve un resultado y puede arrojar una excepción. Los implementadores definen un método único sin argumentos llamado llamada. La Callable
interfaz es similar Runnable
, ya que ambas están diseñadas para clases cuyas instancias son potencialmente ejecutadas por otro hilo. A Runnable
, sin embargo, no devuelve un resultado y no puede lanzar una excepción marcada.
Otras diferencias
Puede pasar Runnable
para crear un hilo . Pero no puede crear un nuevo hilo pasando Callable
como parámetro. Puede pasar invocable solo a ExecutorService
instancias.
public class HelloRunnable implements Runnable {
public void run() {
System.out.println("Hello from a thread!");
}
public static void main(String args[]) {
(new Thread(new HelloRunnable())).start();
}
}
Úselo Runnable
para disparar y olvidar llamadas. Use Callable
para verificar el resultado.
Callable
se puede pasar al método invokeAll a diferencia de Runnable
. Métodos invokeAny
y invokeAll
realizar las formas más comunes de ejecución masiva, ejecutar una colección de tareas y luego esperar al menos una, o todas, para completar
Diferencia trivial: nombre del método a implementar => run()
para Runnable
y call()
para Callable
.
Como ya se mencionó aquí, Callable es una interfaz relativamente nueva y se introdujo como parte del paquete de concurrencia. Tanto Callable como Runnable se pueden usar con ejecutores. Class Thread (que implementa Runnable) solo admite Runnable.
Todavía puede usar Runnable con ejecutores. La ventaja de Callable es que puede enviarlo al ejecutor e inmediatamente recuperar el resultado futuro que se actualizará cuando finalice la ejecución. Lo mismo puede implementarse con Runnable, pero en este caso debe administrar los resultados usted mismo. Por ejemplo, puede crear una cola de resultados que contendrá todos los resultados. Otro hilo puede esperar en esta cola y lidiar con los resultados que llegan.
Future
o agregando gancho que capture todas las excepciones no adquiridas
Thread
para hacer un uso significativo de la Callable
interfaz para que un solo hilo se pueda personalizar para hacer cosas invocables y otras cosas que el desarrollador pueda desear. Si alguien que lee este comentario piensa que estoy equivocado, me gustaría saberlo mejor ...
+-------------------------------------+--------------------------------------------------------------------------------------------------+
| Runnable | Callable<T> |
+-------------------------------------+--------------------------------------------------------------------------------------------------+
| Introduced in Java 1.0 of java.lang | Introduced in Java 1.5 of java.util.concurrent library |
| Runnable cannot be parametrized | Callable is a parametrized type whose type parameter indicates the return type of its run method |
| Runnable has run() method | Callable has call() method |
| Runnable.run() returns void | Callable.call() returns a value of Type T |
| Can not throw Checked Exceptions | Can throw Checked Exceptions |
+-------------------------------------+--------------------------------------------------------------------------------------------------+
Los diseñadores de Java sintieron la necesidad de ampliar las capacidades de la Runnable
interfaz, pero no querían afectar los usos de la Runnable
interfaz y probablemente esa fue la razón por la que optaron por tener una interfaz separada nombrada Callable
en Java 1.5 en lugar de cambiar Runnable
interfaz existente que ha sido parte de Java desde Java 1.0. fuente
Las diferencias entre Callable y Runnable son las siguientes:
Callable y Runnable, ambos son similares entre sí y pueden usarse para implementar hilos. En el caso de implementar Runnable , debe implementar el método run () , pero en el caso de invocables, debe implementar el método call () , ambos métodos funcionan de manera similar, pero el método invocable call () tiene más flexibilidad. Hay algunas diferencias entre ellos.
Diferencia entre Runnable y invocable como a continuación:
1) El método run () de runnable devuelve void , significa que si desea que su hilo devuelva algo que pueda usar aún más, entonces no tiene otra opción con el método Runnable run () . Hay una solución 'Callable' . Si desea devolver algo en forma de objeto , debe usar Callable en lugar de Runnable . La interfaz invocable tiene el método 'call ()' que devuelve Object .
Firma del método - Ejecutable->
public void run(){}
Llamable->
public Object call(){}
2) En caso de ejecución Ejecutable () método si ninguna excepción comprobada surge entonces debe ser pasadas de bloque con intento de captura , pero en caso de llamada rescatable () método que puede lanzar excepción comprobada la siguiente
public Object call() throws Exception {}
3) Runnable proviene de la versión legacy java 1.0 , pero invocable vino en la versión Java 1.5 con el marco Executer .
Si está familiarizado con Executers, entonces debe usar Callable en lugar de Runnable .
Espero que entiendas.
Runnable (vs) Callable entra en juego cuando usamos el framework Executer.
ExecutorService es una subinterfaz de Executor
, que acepta tareas ejecutables y ejecutables.
Se puede lograr un subproceso múltiple anterior utilizando la interfaz desde 1.0 , pero aquí el problema es después de completar la tarea de subproceso, no podemos recopilar la información de subprocesos. Para recopilar los datos, podemos usar campos estáticos.Runnable
Ejemplo Hilos separados para recopilar datos de cada alumno.
static HashMap<String, List> multiTasksData = new HashMap();
public static void main(String[] args) {
Thread t1 = new Thread( new RunnableImpl(1), "T1" );
Thread t2 = new Thread( new RunnableImpl(2), "T2" );
Thread t3 = new Thread( new RunnableImpl(3), "T3" );
multiTasksData.put("T1", new ArrayList() ); // later get the value and update it.
multiTasksData.put("T2", new ArrayList() );
multiTasksData.put("T3", new ArrayList() );
}
Para resolver este problema, han introducido Since 1.5 que devuelve un resultado y puede arrojar una excepción.Callable<V>
Método abstracto único : tanto la interfaz invocable como la ejecutable tienen un único método abstracto, lo que significa que pueden usarse en expresiones lambda en java 8.
public interface Runnable {
public void run();
}
public interface Callable<Object> {
public Object call() throws Exception;
}
Hay algunas formas diferentes de delegar tareas para su ejecución a un ExecutorService .
execute(Runnable task):void
crea un nuevo hilo pero no bloquea el hilo principal o el hilo de la persona que llama, ya que este método devuelve nulo.submit(Callable<?>):Future<?>
, submit(Runnable):Future<?>
crea un nuevo hilo y bloquea el hilo principal cuando está utilizando future.get () .Ejemplo de uso de interfaces ejecutables, invocables con el marco de ejecución.
class CallableTask implements Callable<Integer> {
private int num = 0;
public CallableTask(int num) {
this.num = num;
}
@Override
public Integer call() throws Exception {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + " : Started Task...");
for (int i = 0; i < 5; i++) {
System.out.println(i + " : " + threadName + " : " + num);
num = num + i;
MainThread_Wait_TillWorkerThreadsComplete.sleep(1);
}
System.out.println(threadName + " : Completed Task. Final Value : "+ num);
return num;
}
}
class RunnableTask implements Runnable {
private int num = 0;
public RunnableTask(int num) {
this.num = num;
}
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + " : Started Task...");
for (int i = 0; i < 5; i++) {
System.out.println(i + " : " + threadName + " : " + num);
num = num + i;
MainThread_Wait_TillWorkerThreadsComplete.sleep(1);
}
System.out.println(threadName + " : Completed Task. Final Value : "+ num);
}
}
public class MainThread_Wait_TillWorkerThreadsComplete {
public static void main(String[] args) throws InterruptedException, ExecutionException {
System.out.println("Main Thread start...");
Instant start = java.time.Instant.now();
runnableThreads();
callableThreads();
Instant end = java.time.Instant.now();
Duration between = java.time.Duration.between(start, end);
System.out.format("Time taken : %02d:%02d.%04d \n", between.toMinutes(), between.getSeconds(), between.toMillis());
System.out.println("Main Thread completed...");
}
public static void runnableThreads() throws InterruptedException, ExecutionException {
ExecutorService executor = Executors.newFixedThreadPool(4);
Future<?> f1 = executor.submit( new RunnableTask(5) );
Future<?> f2 = executor.submit( new RunnableTask(2) );
Future<?> f3 = executor.submit( new RunnableTask(1) );
// Waits until pool-thread complete, return null upon successful completion.
System.out.println("F1 : "+ f1.get());
System.out.println("F2 : "+ f2.get());
System.out.println("F3 : "+ f3.get());
executor.shutdown();
}
public static void callableThreads() throws InterruptedException, ExecutionException {
ExecutorService executor = Executors.newFixedThreadPool(4);
Future<Integer> f1 = executor.submit( new CallableTask(5) );
Future<Integer> f2 = executor.submit( new CallableTask(2) );
Future<Integer> f3 = executor.submit( new CallableTask(1) );
// Waits until pool-thread complete, returns the result.
System.out.println("F1 : "+ f1.get());
System.out.println("F2 : "+ f2.get());
System.out.println("F3 : "+ f3.get());
executor.shutdown();
}
}
Es una especie de convención de nomenclatura de interfaz que coincide con la programación funcional.
//Runnable
interface Runnable {
void run();
}
//Action - throws exception
interface Action {
void run() throws Exception;
}
//Consumer - consumes a value/values, throws exception
interface Consumer1<T> {
void accept(T t) throws Exception;
}
//Callable - return result, throws exception
interface Callable<R> {
R call() throws Exception;
}
//Supplier - returns result, throws exception
interface Supplier<R> {
R get() throws Exception;
}
//Predicate - consumes a value/values, returns true or false, throws exception
interface Predicate1<T> {
boolean test(T t) throws Exception;
}
//Function - consumes a value/values, returns result, throws exception
public interface Function1<T, R> {
R apply(T t) throws Throwable;
}
...