Pregunta

¿Cuál es la diferencia entre usar el Runnable y Callable interfaces al diseñar un hilo concurrente en Java, ¿por qué elegirías una sobre la otra?

¿Fue útil?

Solución

Ver explicación aquí .

  

La interfaz invocable es similar a   Runnable, ya que ambos están diseñados   para clases cuyas instancias son   potencialmente ejecutado por otro   hilo. Un Runnable, sin embargo, no   devuelve un resultado y no puede lanzar un   excepción marcada.

Otros consejos

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

Básicamente sí.Ver las respuestas a esta pregunta.Y el javadoc para Callable.

¿Cuál es la necesidad de tener ambos si? Callable puede hacer todo eso Runnable ¿hace?

Porque el Runnable interfaz no puedo hacer todo lo que Callable ¡hace!

Runnable ha existido desde Java 1.0, pero Callable sólo se introdujo en Java 1.5...para manejar casos de uso que Runnable no soporta.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 una recodificación al migrar código Java antiguo a 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 en los que una tarea no necesidad para devolver un resultado o lanzar una excepción marcada.Para esos casos de uso, usando Runnable es más conciso que usar Callable<Void> y devolver un muñeco (null) valor de la call() método.

  • A Callable necesita implementar el método call() mientras que Runnable necesita implementar el método run().
  • A ExecutorService#invokeXXX(Collection<? extends Callable<T>> tasks) puede devolver un valor pero un <=> no puede.
  • Un <=> puede lanzar una excepción marcada pero un <=> no puede.
  • A <=> se puede usar con <=> métodos, pero no se puede <=>.

    public interface Runnable {
        void run();
    }
    
    public interface Callable<V> {
        V call() throws Exception;
    }
    

Encontré esto en otro blog que puede explicarlo un poco más estos diferencias:

Aunque ambas interfaces son implementadas por las clases que desean ejecutar en un hilo de ejecución diferente, existen pocas diferencias entre las dos interfaces, que son:

  • A Callable<V> la instancia devuelve un resultado de tipo V, mientras que un Runnable la instancia no lo hace.
  • A Callable<V> una instancia puede arrojar excepciones comprobadas, mientras que una Runnable la instancia no puede

Los diseñadores de Java sintieron la necesidad de ampliar las capacidades del Runnable interfaz, pero no querían afectar los usos del Runnable interfaz y probablemente esa fue la razón por la que optaron por tener una interfaz separada llamada Callable en Java 1.5 que cambiar lo ya 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 una tarea de incendio y olvido, use Runnable. Coloque su código dentro de un Runnable y cuando se llame al método run (), puede realizar su tarea. Al hilo de llamada realmente no le importa cuando realiza su tarea.

Callable : si está intentando recuperar un valor de una tarea, use Callable. 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.

Entonces, piense en una interfaz para una clase de destino donde tenga definidos los métodos ajustables Runnable y Callable. La clase que llama al azar llamará 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 solo 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 call() método y debe proporcionar genéricos ya que el tipo de llamada de objeto () 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 está la interfaz que declara run() método que se llama cuando crea un subproceso con el ejecutable y llama a start() en él.También puede llamar directamente a run() pero eso solo ejecuta el método run() en 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. A Runnable objeto no devuelve un resultado mientras que un Callable objeto devuelve un resultado.
  2. A Runnable El objeto no puede lanzar una excepción marcada mientras que una Callable objeto puede lanzar un excepción.
  3. El Runnable La interfaz ha existido desde Java 1.0, mientras que Callable sólo se introdujo en Java 1.5.

Pocas similitudes incluyen

  1. Las instancias de las clases que implementan interfaces Runnable o Callable son potencialmente ejecutado por otro hilo.
  2. ExecutorService puede ejecutar instancias de las interfaces invocables y ejecutables mediante el método submit().
  3. Ambas son interfaces funcionales y se pueden utilizar 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);

Propósito de estas interfaces de la documentación de Oracle:

Ejecutable La interfaz debe ser implementada por cualquier clase cuyas instancias estén destinadas a ser ejecutadas por un Thread.La clase debe definir un método sin argumentos llamado run.

invocable:Una tarea que devuelve un resultado y puede generar una excepción.Los implementadores definen un método único sin argumentos llamado llamada.El Callable La interfaz es similar a Runnable, en el sentido de que ambos están diseñados para clases cuyas instancias son potencialmente ejecutadas por otro hilo.A Runnable, sin embargo, no devuelve un resultado y no puede generar una excepción marcada.

Otras diferencias:

  1. Puedes pasar Runnable para crear un Hilo.Pero no puedes crear un nuevo hilo pasando Callable como parámetro.Puedes pasar Callable solo a ExecutorService instancias.

    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. Usar Runnable para llamadas de fuego y olvido.Usar Callable para verificar el resultado.

  3. Callable se puede pasar a invocar todo método diferente Runnable.Métodos invokeAny y invokeAll Realizar las formas más comúnmente útiles de ejecución masiva, ejecutando una colección de tareas y luego esperando a que se complete al menos una, o todas.

  4. 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. El subproceso de clase (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.

+-------------------------------------+--------------------------------------------------------------------------------------------------+
|              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 del Runnable interfaz, pero no querían afectar los usos del Runnable interfaz y probablemente esa fue la razón por la que optaron por tener una interfaz separada llamada Callable en Java 1.5 que cambiar lo ya existente Runnable interfaz que ha sido parte de Java desde Java 1.0. fuente

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 marcada pero el método de ejecución no puede lanzar una excepción marcada.
  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.

Callable y Runnable ambos son similares entre sí y pueden usarse en la implementación del hilo. En caso de implementar Runnable , debe implementar el método run () , pero en caso de invocarse, debe implementar el método call () , ambos métodos funciona de manera similar, pero el método call () invocable tiene más flexibilidad. Hay algunas diferencias entre ellos.

Diferencia entre Ejecutable y invocable como se muestra a continuación--

1) El método run () de runnable devuelve void , lo que significa que si desea que su hilo devuelva algo que pueda usar más adelante no tiene opción con el método Runnable run () . Hay una solución 'Callable' , si desea devolver cualquier cosa en forma de objeto , entonces debe usar Callable en lugar de Runnable . La interfaz invocable tiene el método 'call ()' que devuelve Object .

Firma del método - Ejecutable - & Gt;

public void run(){}

Llamable - >

public Object call(){}

2) En el caso del método Runnable run () si surge alguna excepción marcada, entonces debe tener que manejarlo con try catch block , pero en el caso de Llamada invocable () método que puede lanzar una excepción marcada como se muestra a continuación

 public Object call() throws Exception {}

3) Runnable proviene de la versión heredada de java 1.0 , pero invocable vino en la versión Java 1.5 con < strong> Executer framework.

Si está familiarizado con Executers , entonces debe usar Callable en lugar de Runnable .

Espero que entiendas.

Ejecutable (vs) invocable entra en juego cuando usamos el marco Executer.

ExecutorService es una subinterfaz de Executor , que acepta tareas ejecutables y ejecutables.

Se pueden lograr múltiples subprocesos anteriores utilizando la interfaz Runnable Desde 1.0 , pero aquí el problema es después de completar la tarea de subproceso no pueden recopilar la información de subprocesos. Para recopilar los datos, podemos usar campos estáticos.

Ejemplo de hilos separados para recopilar los 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 Callable<V> Since 1.5 que devuelve un resultado y puede arrojar una excepción.

  • Método abstracto único : tanto la interfaz invocable como la ejecutable tienen un método abstracto único, 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 en 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();
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top