La diferencia entre las interfaces ejecutables y invocables en Java


Respuestas:


444

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.


270

¿Cuáles son las diferencias en las aplicaciones de Runnabley Callable. ¿La diferencia solo está presente con el parámetro de retorno Callable?

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 que Runnablehace?

¡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.


99
Me pregunto de dónde sacaste esta historia. Esto es muy útil
Spiderman

44
@prash: los datos básicos se encuentran en los libros de texto antiguos. Como la primera edición de Java in a Nutshell.
Stephen C

44
(@prash - También ... al comenzar a usar Java en la era Java 1.1.)
Stephen C

1
@StephenC Si leí tu respuesta correctamente, estás sugiriendo que 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?
max

1
@max - Bueno, dije eso, y todavía estoy de acuerdo con eso. Sin embargo, esa es una razón secundaria. Pero aun así, sospecho que 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.)
Stephen C

82
  • A Callablenecesita implementar el call()método mientras que Runnablenecesita implementar el run()método.
  • A Callablepuede devolver un valor pero a Runnableno puede.
  • A 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;
    }
    

17
ExecutorService.submit (tarea ejecutable) también existe y es muy útil
Yair Kukielka

Runnable también se puede usar con ExecutorService de la siguiente manera: 1) ExecutorService.execute (Runnable) 2) ExecutorService.submit (Runnable)
Azam Khan

2
También hay Executor.submit (tarea invocable <T>) pero no puede invocar All o invokeAny con la colección de tareas ejecutables Collection <? extiende las tareas <T>>
invocables

36

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:

  • Una Callable<V>instancia devuelve un resultado de tipo V, mientras que una Runnableinstancia no.
  • Una Callable<V>instancia puede arrojar excepciones marcadas, mientras que una Runnableinstancia no puede

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 existente Runnable.


27

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.


14

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

  1. Un Runnableobjeto no devuelve un resultado, mientras que un Callableobjeto devuelve un resultado.
  2. Un Runnableobjeto no puede lanzar una excepción marcada cuando un Callableobjeto puede lanzar una excepción.
  3. La Runnableinterfaz ha existido desde Java 1.0, mientras que Callablesolo se introdujo en Java 1.5.

Pocas similitudes incluyen

  1. Las instancias de las clases que implementan interfaces ejecutables o invocables son ejecutadas potencialmente por otro subproceso.
  2. ExecutorService puede ejecutar la instancia de las interfaces ejecutables y ejecutables mediante el método submit ().
  3. Ambas son interfaces funcionales y se pueden usar en expresiones Lambda desde Java8.

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

13

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

  1. Puede pasar Runnablepara crear un hilo . Pero no puede crear un nuevo hilo pasando Callablecomo parámetro. Puede pasar invocable solo a ExecutorServiceinstancias.

    Ejemplo:

    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();
        }
    
    }
  2. Úselo Runnablepara disparar y olvidar llamadas. Use Callablepara verificar el resultado.

  3. 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

  4. Diferencia trivial: nombre del método a implementar => run()para Runnabley call()para Callable.


11

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.


Me pregunto cuál es el ejemplo en un hilo lanzando una excepción en Java. ¿podrá el hilo principal captar esa excepción? Si no, no usaría Callable. Alex, ¿tienes alguna idea sobre esto? ¡Gracias!
trillones

1
El código que se ejecuta en un subproceso personalizado como cualquier otro código puede generar una excepción. Para atraparlo en otro hilo, debe realizar algunos esfuerzos, ya sea mediante el uso de un mecanismo de notificación personalizado (por ejemplo, basado en oyentes) o mediante el uso Futureo agregando gancho que capture todas las excepciones no adquiridas
AlexR

Gran información! Gracias Alex! :)
billones

1
Voté esta respuesta porque afirma (correctamente si se toma al pie de la letra) uno debe usar el modelo de grupo de subprocesos con objetos invocables. Lo aparentemente desafortunado de esto es que no se puede extender 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 ...

8
+-------------------------------------+--------------------------------------------------------------------------------------------------+
|              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


7

Las diferencias entre Callable y Runnable son las siguientes:

  1. Callable se introduce en JDK 5.0 pero Runnable se introduce en JDK 1.0
  2. Callable tiene el método call () pero Runnable tiene el método run ().
  3. Callable tiene un método de llamada que devuelve un valor, pero Runnable tiene un método de ejecución que no devuelve ningún valor.
  4. el método de llamada puede lanzar una excepción verificada, pero el método de ejecución no puede lanzar una excepción verificada.
  5. Se puede usar el método submit () para poner en la cola de tareas, pero Runnable usa el método execute () para poner en la cola de tareas.

Es importante enfatizar que la Excepción marcada , no la RuntimeException
BertKing

5

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.


2

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

0

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

...
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.