La différence entre les interfaces Runnable et Callable en Java


Réponses:


444

Voir l'explication ici .

L'interface Callable est similaire à Runnable, en ce sens que les deux sont conçues pour les classes dont les instances sont potentiellement exécutées par un autre thread. Un Runnable, cependant, ne renvoie pas de résultat et ne peut pas lever d'exception vérifiée.


270

Quelles sont les différences dans les applications de Runnableet Callable. La différence est-elle uniquement avec le paramètre de retour présent dans Callable?

Fondamentalement, oui. Voir les réponses à cette question . Et le javadoc pourCallable .

Quel est le besoin d'avoir les deux si l' Callableon peut faire tout ce qui Runnablefait?

Parce que l' Runnableinterface ne peut pas tout faire Callable!

Runnableexiste depuis Java 1.0, mais Callablen'a été introduit que dans Java 1.5 ... pour gérer les cas d'utilisation qui Runnablene sont pas pris en charge. En théorie, l'équipe Java aurait pu changer la signature de la Runnable.run()méthode, mais cela aurait rompu la compatibilité binaire avec le code antérieur à 1.5, nécessitant un recodage lors de la migration de l'ancien code Java vers des machines virtuelles Java plus récentes. C'est un GRAND NON-NON. Java s'efforce d'être rétrocompatible ... et c'est l'un des principaux arguments de vente de Java pour l'informatique d'entreprise.

Et, évidemment, il existe des cas d'utilisation où une tâche n'a pas besoin de retourner un résultat ou de lever une exception vérifiée. Pour ces cas d'utilisation, l'utilisation Runnableest plus concise que l'utilisation Callable<Void>et le retour d'une nullvaleur fictive ( ) à partir de la call()méthode.


9
Je me demande d'où vous est venue cette histoire. C'est très utile.
spiderman

4
@prash - les faits de base se trouvent dans les anciens manuels. Comme la première édition de Java in a Nutshell.
Stephen C

4
(@prash - Aussi ... en commençant à utiliser Java à l'ère de Java 1.1.)
Stephen C

1
@StephenC Si j'ai bien lu votre réponse, vous suggérez qu'elle Runnableexiste (en grande partie) pour des raisons de compatibilité descendante. Mais n'y a-t-il pas des situations où il est inutile ou trop coûteux d'implémenter (ou d'exiger) une Callableinterface (par exemple, en ScheduledFuture<?> ScheduledExecutorService.schedule(Runnable command, long delay, TimeUnit unit))? N'y a-t-il donc aucun avantage à maintenir les deux interfaces dans le langage, même si l'histoire n'a pas forcé le résultat actuel?
max

1
@max - Eh bien, je l'ai dit et je suis toujours d'accord avec cela. Cependant, c'est une raison secondaire. Mais même ainsi, je soupçonne que Runnable cela aurait été modifié s'il n'y avait pas eu impératif de maintenir la compatibilité. Le «passe-partout» de return null;est un argument faible. (Au moins, cela aurait été ma décision ... dans le contexte hypothétique où vous pourriez ignorer la compatibilité descendante.)
Stephen C

82
  • Un Callabledoit implémenter une call()méthode tandis qu'un a Runnablebesoin d'implémenter une run()méthode.
  • A Callablepeut renvoyer une valeur mais Runnablepas.
  • Un Callablepeut lever l'exception vérifiée mais Runnablepas.
  • A Callablepeut être utilisé avec des ExecutorService#invokeXXX(Collection<? extends Callable<T>> tasks)méthodes mais Runnablepas.

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

17
ExecutorService.submit (tâche exécutable) existe également et est très utile
Yair Kukielka

Runnable peut également être utilisé avec ExecutorService de la manière suivante: 1) ExecutorService.execute (Runnable) 2) ExecutorService.submit (Runnable)
Azam Khan

2
Il existe également Executor.submit (tâche Callable <T>) mais vous ne pouvez pas invokeAll ou invokeAny avec la collection de tâches Runnable Collection <? étend les tâches
Callable

36

J'ai trouvé cela dans un autre blog qui peut expliquer un peu plus ces différences :

Bien que les deux interfaces soient implémentées par les classes qui souhaitent s'exécuter dans un thread d'exécution différent, mais il y a peu de différences entre les deux interfaces qui sont:

  • Une Callable<V>instance renvoie un résultat de type V, contrairement à une Runnableinstance.
  • Une Callable<V>instance peut lever des exceptions vérifiées, tandis qu'une Runnableinstance ne peut pas

Les concepteurs de Java ont ressenti le besoin d'étendre les capacités de l' Runnableinterface, mais ils ne voulaient pas affecter les utilisations de l' Runnableinterface et c'est probablement la raison pour laquelle ils ont opté pour une interface distincte nommée Callabledans Java 1.5 plutôt que de changer la déjà existant Runnable.


27

Voyons où l'on pourrait utiliser Runnable et Callable.

Runnable et Callable s'exécutent tous les deux sur un thread différent du thread appelant. Mais Callable peut renvoyer une valeur et Runnable ne le peut pas. Alors, où cela s'applique-t-il vraiment?

Runnable : Si vous avez un feu et oubliez la tâche, utilisez Runnable. Placez votre code dans un Runnable et lorsque la méthode run () est appelée, vous pouvez effectuer votre tâche. Le thread appelant ne se soucie vraiment pas lorsque vous effectuez votre tâche.

Callable : Si vous essayez de récupérer une valeur d'une tâche, utilisez Callable. Désormais appelable seul, il ne fera pas l'affaire. Vous aurez besoin d'un Futur que vous enroulerez autour de votre Callable et obtiendrez vos valeurs sur future.get (). Ici, le thread appelant sera bloqué jusqu'à ce que le futur revienne avec des résultats qui à leur tour attendent l'exécution de la méthode call () de Callable.

Pensez donc à une interface vers une classe cible dans laquelle vous avez défini à la fois des méthodes encapsulées Runnable et Callable. La classe appelante appellera aléatoirement vos méthodes d'interface sans savoir laquelle est Runnable et laquelle est Callable. Les méthodes Runnable s'exécuteront de manière asynchrone, jusqu'à ce qu'une méthode Callable soit appelée. Ici, le thread de la classe appelante se bloquera car vous récupérez des valeurs de votre classe cible.

REMARQUE: à l'intérieur de votre classe cible, vous pouvez effectuer les appels à Callable et Runnable sur un exécuteur de thread unique, ce qui rend ce mécanisme similaire à une file d'attente de répartition en série. Donc, tant que l'appelant appelle vos méthodes encapsulées Runnable, le thread appelant s'exécutera très rapidement sans blocage. Dès qu'il appelle une méthode Callable enveloppée dans Future, il devra bloquer jusqu'à ce que tous les autres éléments en file d'attente soient exécutés. Ce n'est qu'alors que la méthode renverra des valeurs. Il s'agit d'un mécanisme de synchronisation.


14

Callablel'interface déclare la call()méthode et vous devez fournir des génériques comme type d'appel Object () devrait retourner -

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

Runnabled'autre part, est l'interface qui déclare la run()méthode qui est appelée lorsque vous créez un thread avec runnable et appelez start () dessus. Vous pouvez également appeler directement run () mais qui exécute simplement la méthode run () est le même thread.

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

Pour résumer, quelques différences notables sont

  1. Un Runnableobjet ne renvoie pas de résultat alors qu'un Callableobjet renvoie un résultat.
  2. Un Runnableobjet ne peut pas lever d'exception vérifiée alors qu'un Callableobjet peut lever une exception.
  3. L' Runnableinterface existe depuis Java 1.0 alors qu'elle Callablen'a été introduite que dans Java 1.5.

Peu de similitudes comprennent

  1. Les instances des classes qui implémentent des interfaces Runnable ou Callable sont potentiellement exécutées par un autre thread.
  2. L'instance des interfaces Callable et Runnable peut être exécutée par ExecutorService via la méthode submit ().
  3. Les deux sont des interfaces fonctionnelles et peuvent être utilisées dans les expressions Lambda depuis Java8.

Les méthodes de l'interface ExecutorService sont

<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
<T> Future<T> submit(Runnable task, T result);

13

Objectif de ces interfaces à partir de la documentation Oracle:

L' interface exécutable doit être implémentée par n'importe quelle classe dont les instances sont destinées à être exécutées par a Thread. La classe doit définir une méthode sans arguments appelés run.

Callable : tâche qui renvoie un résultat et peut lever une exception. Les implémenteurs définissent une méthode unique sans argument appelé appel. L' Callableinterface est similaire à Runnable, car les deux sont conçues pour les classes dont les instances sont potentiellement exécutées par un autre thread. A Runnable, cependant, ne renvoie pas de résultat et ne peut pas lever d'exception vérifiée.

Autres différences:

  1. Vous pouvez passer Runnablepour créer un fil . Mais vous ne pouvez pas créer de nouveau Thread en passant Callablecomme paramètre. Vous ne pouvez transmettre Callable qu'aux ExecutorServiceinstances.

    Exemple:

    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. Utilisez Runnablepour le feu et oubliez les appels. Utilisez Callablepour vérifier le résultat.

  3. Callablepeut être passé à la méthode invokeAll contrairement Runnable. Méthodes invokeAnyet invokeAllexécution des formes d'exécution en masse les plus couramment utilisées, exécution d'une collection de tâches, puis attente d'au moins une, ou de toutes, pour terminer

  4. Différence triviale: nom de la méthode à implémenter => run()pour Runnableet call()pour Callable.


11

Comme il a déjà été mentionné ici, Callable est une interface relativement nouvelle et elle a été introduite dans le cadre du package de concurrence. Callable et Runnable peuvent être utilisés avec des exécuteurs. Le thread de classe (qui implémente Runnable lui-même) ne prend en charge que Runnable.

Vous pouvez toujours utiliser Runnable avec des exécuteurs. L'avantage de Callable que vous pouvez l'envoyer à l'exécuteur testamentaire et récupérer immédiatement le résultat futur qui sera mis à jour lorsque l'exécution sera terminée. La même chose peut être implémentée avec Runnable, mais dans ce cas, vous devez gérer les résultats vous-même. Par exemple, vous pouvez créer une file d'attente de résultats qui contiendra tous les résultats. Un autre thread peut attendre dans cette file d'attente et gérer les résultats qui arrivent.


Je me demande quel est l'exemple d'une exception de lancement de thread en Java? le thread principal pourra-t-il intercepter cette exception? Sinon, je n'utiliserais pas Callable. Alex, avez-vous un aperçu de cela? Merci!
billions

1
Le code s'exécutant dans un thread personnalisé comme tout autre code peut lever l'exception. Pour l'attraper dans un autre thread, vous devez effectuer certains efforts soit en utilisant un mécanisme de notification personnalisé (par exemple basé sur des écouteurs), soit en utilisant Futureou en ajoutant un crochet qui intercepte toutes les exceptions non identifiées
AlexR

Grande info! Merci, Alex! :)
billions

1
J'ai voté pour cette réponse car elle affirme (correctement si elle est prise à sa valeur nominale) que l'on doit utiliser le modèle de pool de threads avec des objets appelables. La chose apparemment malheureuse à ce sujet est que l'on ne peut pas s'étendre Threadpour faire un usage significatif de l' Callableinterface afin qu'un seul thread puisse être personnalisé pour faire des choses appelables et d'autres choses que le développeur pourrait vouloir. Si quelqu'un qui lit ce commentaire pense que je me trompe, j'aimerais mieux savoir ...

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

Les concepteurs de Java ont ressenti le besoin d'étendre les capacités de l' Runnableinterface, mais ils ne voulaient pas affecter les utilisations de l' Runnableinterface et c'est probablement la raison pour laquelle ils ont opté pour une interface distincte nommée Callabledans Java 1.5 plutôt que de changer la déjà Runnableinterface existante qui fait partie de Java depuis Java 1.0. la source


7

Les différences entre Callable et Runnable sont les suivantes:

  1. Callable est introduit dans JDK 5.0 mais Runnable est introduit dans JDK 1.0
  2. Callable a la méthode call () mais Runnable a la méthode run ().
  3. Callable a une méthode d'appel qui renvoie une valeur mais Runnable a une méthode d'exécution qui ne renvoie aucune valeur.
  4. la méthode d'appel peut lever l'exception vérifiée mais la méthode d'exécution ne peut pas lever l'exception vérifiée.
  5. Appelable utilise la méthode submit () pour placer dans la file d'attente des tâches mais Runnable utilise la méthode execute () pour placer dans la file d'attente des tâches.

Il est important de souligner que l' exception vérifiée , pas l' exception RuntimeException
BertKing

5

Callable et Runnable sont tous deux similaires et peuvent être utilisés pour implémenter le thread. En cas d'implémentation de Runnable, vous devez implémenter la méthode run () , mais en cas d' appel, vous devez implémenter la méthode call () , les deux méthodes fonctionnent de manière similaire, mais la méthode callable call () a plus de flexibilité.Il y a quelques différences entre elles.

Différence entre Runnable et callable comme ci-dessous -

1) La méthode run () de runnable renvoie void , signifie que si vous voulez que votre thread retourne quelque chose que vous pouvez utiliser plus loin, vous n'avez pas le choix avec la méthode runnable run () . Il existe une solution «Callable» , si vous voulez renvoyer quelque chose sous forme d' objet, vous devez utiliser Callable au lieu de Runnable . L'interface appelable a la méthode 'call ()' qui retourne Object .

Signature de méthode - Runnable->

public void run(){}

Appelable->

public Object call(){}

2) Dans le cas de la méthode runnable run () si une exception vérifiée survient, vous devez alors gérer avec le bloc try catch , mais dans le cas de la méthode Callable call () , vous pouvez lever l'exception vérifiée comme ci-dessous

 public Object call() throws Exception {}

3) Runnable provient de la version java 1.0 héritée , mais callable est venu en version Java 1.5 avec le cadre Executer .

Si vous connaissez les exécuteurs, vous devez utiliser Callable au lieu de Runnable .

J'espère que tu as compris.


2

Runnable (vs) Callable entre en jeu lorsque nous utilisons le framework Executer.

ExecutorService est une sous-interface de Executor, qui accepte les tâches exécutables et appelables.

Le multi-threading antérieur peut être réalisé à l'aide de l'interface depuis la version 1.0 , mais ici, le problème est qu'après avoir terminé la tâche de thread, nous ne sommes pas en mesure de collecter les informations sur les threads. Afin de collecter les données, nous pouvons utiliser des champs statiques.Runnable

Exemple Fils de discussion séparés pour collecter les données de chaque élève.

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

Pour résoudre ce problème, ils ont introduit la version 1.5 qui renvoie un résultat et peut lever une exception.Callable<V>

  • Méthode abstraite unique : l'interface Callable et Runnable ont une seule méthode abstraite, ce qui signifie qu'elles peuvent être utilisées dans les expressions lambda en java 8.

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

Il existe différentes manières de déléguer des tâches pour exécution à un ExecutorService .

  • execute(Runnable task):void crée un nouveau thread mais ne bloque pas le thread principal ou le thread appelant car cette méthode retourne void.
  • submit(Callable<?>):Future<?>, submit(Runnable):Future<?>crée un nouveau thread et bloque le thread principal lorsque vous utilisez future.get () .

Exemple d'utilisation d'interfaces exécutables, appelables avec le framework Executor.

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

C'est une sorte de convention de dénomination d'interface qui correspond à la programmation fonctionnelle

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

...
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.