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
RunnableyCallable. ¿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
Callablepuede hacer todo lo queRunnablehace?
¡Porque la Runnableinterfaz no puede hacer todo lo que Callablehace!
Runnableha existido desde Java 1.0, pero Callablesolo se introdujo en Java 1.5 ... para manejar casos de uso que Runnableno 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 Runnablees más conciso que usar Callable<Void>y devolver un nullvalor ficticio ( ) del call()método.
Runnableexiste (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 Callableinterfaz (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.)
Callablenecesita implementar el call()método mientras que Runnablenecesita implementar el run()método.Callablepuede devolver un valor pero a Runnableno puede.Callablepuede lanzar una excepción marcada pero Runnableno puede.A Callablese puede usar con ExecutorService#invokeXXX(Collection<? extends Callable<T>> tasks)métodos pero a Runnableno 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 Runnableinstancia no.Callable<V>instancia puede arrojar excepciones marcadas, mientras que una Runnableinstancia no puedeLos diseñadores de Java sintieron la necesidad de ampliar las capacidades de la Runnableinterfaz, pero no querían afectar los usos de la Runnableinterfaz y probablemente esa fue la razón por la que optaron por tener una interfaz separada nombrada Callableen 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.
CallableLa 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;
}
RunnablePor 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
Runnableobjeto no devuelve un resultado, mientras que un Callableobjeto devuelve un resultado.Runnableobjeto no puede lanzar una excepción marcada cuando un Callableobjeto puede lanzar una excepción.Runnableinterfaz ha existido desde Java 1.0, mientras que Callablesolo 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 Callableinterfaz 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 Runnablepara crear un hilo . Pero no puede crear un nuevo hilo pasando Callablecomo parámetro. Puede pasar invocable solo a ExecutorServiceinstancias.
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 Runnablepara disparar y olvidar llamadas. Use Callablepara verificar el resultado.
Callablese puede pasar al método invokeAll a diferencia de Runnable. Métodos invokeAnyy invokeAllrealizar 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 Runnabley 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.
Futureo agregando gancho que capture todas las excepciones no adquiridas
Threadpara hacer un uso significativo de la Callableinterfaz 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 Runnableinterfaz, pero no querían afectar los usos de la Runnableinterfaz y probablemente esa fue la razón por la que optaron por tener una interfaz separada nombrada Callableen Java 1.5 en lugar de cambiar Runnableinterfaz 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;
}
...