Der Unterschied zwischen den Schnittstellen Runnable und Callable in Java

Antworten:

444

Siehe Erklärung hier .

Die Callable-Schnittstelle ähnelt Runnable, da beide für Klassen ausgelegt sind, deren Instanzen möglicherweise von einem anderen Thread ausgeführt werden. Ein Runnable gibt jedoch kein Ergebnis zurück und kann keine aktivierte Ausnahme auslösen.

Jorge Ferreira
quelle
269

Was sind die Unterschiede in den Anwendungen von Runnableund Callable. Ist der Unterschied nur mit dem Rückgabeparameter in vorhanden Callable?

Grundsätzlich ja. Siehe die Antworten auf diese Frage . Und der Javadoc fürCallable .

Was ist die Notwendigkeit, beides zu haben, wenn Callablealles möglich Runnableist?

Weil die RunnableSchnittstelle nicht alles kann , was sie Callabletut!

Runnablegibt es seit Java 1.0, wurde aber Callablenur in Java 1.5 eingeführt ... um Anwendungsfälle zu behandeln, Runnabledie nicht unterstützt werden. Theoretisch hätte das Java-Team die Signatur der Runnable.run()Methode ändern können, dies hätte jedoch die Binärkompatibilität mit Code vor 1.5 beeinträchtigt und eine Neukodierung erforderlich gemacht, wenn alter Java-Code auf neuere JVMs migriert wird. Das ist ein GROSSES NEIN-NEIN. Java ist bestrebt, abwärtskompatibel zu sein ... und das war eines der größten Verkaufsargumente von Java für Business Computing.

Und natürlich gibt es Anwendungsfälle , wo eine Aufgabe nicht brauchen ein Ergebnis oder werfen eine geprüfte Ausnahme zurückzukehren. In diesen Anwendungsfällen ist die Verwendung Runnableprägnanter als die Verwendung Callable<Void>und Rückgabe eines Dummy ( null) - Werts aus der call()Methode.

Stephen C.
quelle
9
Ich frage mich, woher du diese Geschichte hast. Das ist sehr nützlich.
Spiderman
4
@prash - die grundlegenden Fakten sind in alten Lehrbüchern zu finden. Wie die erste Ausgabe von Java auf den Punkt gebracht.
Stephen C
4
(@prash - Auch ... indem Sie anfangen, Java in der Java 1.1-Ära zu verwenden.)
Stephen C
1
@StephenC Wenn ich Ihre Antwort richtig gelesen habe, schlagen Sie vor, dass sie Runnable(weitgehend) aus Gründen der Abwärtskompatibilität existiert. Aber gibt es nicht Situationen, in denen die Implementierung (oder Anforderung) einer CallableSchnittstelle (z. B. in ScheduledFuture<?> ScheduledExecutorService.schedule(Runnable command, long delay, TimeUnit unit)) unnötig oder zu teuer ist ? Gibt es also keinen Vorteil, wenn beide Schnittstellen in der Sprache beibehalten werden, obwohl die Historie das aktuelle Ergebnis nicht erzwungen hat?
Max
1
@max - Nun, das habe ich gesagt, und dem stimme ich immer noch zu. Dies ist jedoch ein sekundärer Grund. Trotzdem vermute ich, dass Runnable dies geändert worden wäre, wenn es keine Notwendigkeit gegeben hätte, die Kompatibilität aufrechtzuerhalten. Das "Boilerplate" von return null;ist ein schwaches Argument. (Zumindest wäre das meine Entscheidung gewesen ... in dem hypothetischen Kontext, in dem man die Abwärtskompatibilität ignorieren könnte.)
Stephen C
82
  • Eine Methode Callablemuss implementiert call()werden, während eine Methode Runnableimplementiert run()werden muss.
  • A Callablekann einen Wert zurückgeben, a Runnablejedoch nicht.
  • A Callablekann eine geprüfte Ausnahme auslösen, A Runnablejedoch nicht.
  • A Callablekann mit ExecutorService#invokeXXX(Collection<? extends Callable<T>> tasks)Methoden verwendet werden, a Runnablejedoch nicht.

    public interface Runnable {
        void run();
    }
    
    public interface Callable<V> {
        V call() throws Exception;
    }
nikli
quelle
17
ExecutorService.submit (Runnable Task) existiert ebenfalls und ist sehr nützlich
Yair Kukielka
Runnable kann auch mit ExecutorService auf folgende Weise verwendet werden: 1) ExecutorService.execute (Runnable) 2) ExecutorService.submit (Runnable)
Azam Khan
2
Es gibt auch Executor.submit (Callable <T> -Aufgabe), aber Sie können nicht All oder InvokeAny mit einer Sammlung von ausführbaren Aufgaben aufrufen. Collection <? erweitert Callable <T>> Aufgaben
Nikli
36

Ich habe dies in einem anderen Blog gefunden, der diese Unterschiede etwas näher erläutern kann :

Obwohl beide Schnittstellen von den Klassen implementiert werden, die in einem anderen Ausführungsthread ausgeführt werden möchten, gibt es nur wenige Unterschiede zwischen den beiden Schnittstellen:

  • Eine Callable<V>Instanz gibt ein Ergebnis vom Typ zurück V, eine RunnableInstanz jedoch nicht.
  • Eine Callable<V>Instanz kann geprüfte Ausnahmen auslösen, eine RunnableInstanz jedoch nicht

Die Entwickler von Java hatten das Bedürfnis, die Funktionen der RunnableSchnittstelle zu erweitern, aber sie wollten die Verwendung der RunnableSchnittstelle nicht beeinflussen , und wahrscheinlich war dies der Grund, warum sie sich für eine separate Schnittstelle mit dem Namen CallableJava 1.5 entschieden haben, anstatt die bereits zu ändern vorhanden Runnable.

Amoran
quelle
27

Schauen wir uns an, wo man Runnable und Callable verwenden würde.

Runnable und Callable werden beide auf einem anderen Thread als dem aufrufenden Thread ausgeführt. Callable kann jedoch einen Wert zurückgeben und Runnable nicht. Wo trifft das wirklich zu?

Runnable : Wenn Sie eine Feuer- und Vergessensaufgabe haben, verwenden Sie Runnable. Fügen Sie Ihren Code in eine ausführbare Datei ein, und wenn die run () -Methode aufgerufen wird, können Sie Ihre Aufgabe ausführen. Dem aufrufenden Thread ist es wirklich egal, wann Sie Ihre Aufgabe ausführen.

Callable : Wenn Sie versuchen, einen Wert aus einer Aufgabe abzurufen, verwenden Sie Callable. Jetzt allein aufrufbar macht den Job nicht. Sie benötigen eine Zukunft, die Sie um Ihr Callable wickeln und Ihre Werte auf future.get () abrufen. Hier wird der aufrufende Thread blockiert, bis die Zukunft mit Ergebnissen zurückkommt, die wiederum auf die Ausführung der call () -Methode von Callable warten.

Stellen Sie sich also eine Schnittstelle zu einer Zielklasse vor, in der sowohl Runnable- als auch Callable-Wrapped-Methoden definiert sind. Die aufrufende Klasse ruft zufällig Ihre Schnittstellenmethoden auf, ohne zu wissen, welche ausführbar und welche aufrufbar ist. Die Runnable-Methoden werden asynchron ausgeführt, bis eine Callable-Methode aufgerufen wird. Hier wird der Thread der aufrufenden Klasse blockiert, da Sie Werte aus Ihrer Zielklasse abrufen.

ANMERKUNG: Innerhalb Ihrer Zielklasse können Sie Callable und Runnable auf einem einzelnen Thread-Executor aufrufen, wodurch dieser Mechanismus einer seriellen Versandwarteschlange ähnelt. Solange der Aufrufer Ihre Runnable-Wrapped-Methoden aufruft, wird der aufrufende Thread sehr schnell ausgeführt, ohne zu blockieren. Sobald eine in Future eingeschlossene Callable-Methode aufgerufen wird, muss sie blockiert werden, bis alle anderen Elemente in der Warteschlange ausgeführt werden. Nur dann wird die Methode mit Werten zurückgegeben. Dies ist ein Synchronisationsmechanismus.

Kris Subramanian
quelle
14

CallableDie Schnittstelle deklariert die call()Methode und Sie müssen Generika angeben, da der Typ des Objektaufrufs () Folgendes zurückgeben sollte:

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

RunnableAuf der anderen Seite befindet sich eine Schnittstelle, die eine run()Methode deklariert , die aufgerufen wird, wenn Sie einen Thread mit der ausführbaren Datei erstellen und start () aufrufen. Sie können run () auch direkt aufrufen, aber das, was nur die run () -Methode ausführt, ist der gleiche 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();
}

Um einige bemerkenswerte Unterschiede zusammenzufassen sind

  1. Ein RunnableObjekt gibt kein Ergebnis zurück, während ein CallableObjekt ein Ergebnis zurückgibt.
  2. Ein RunnableObjekt kann keine aktivierte Ausnahme auslösen, während ein CallableObjekt eine Ausnahme auslösen kann.
  3. Die RunnableSchnittstelle gibt es seit Java 1.0, während sie Callablenur in Java 1.5 eingeführt wurde.

Einige Ähnlichkeiten schließen ein

  1. Instanzen der Klassen, die Runnable- oder Callable-Schnittstellen implementieren, werden möglicherweise von einem anderen Thread ausgeführt.
  2. Die Instanz von Callable- und Runnable-Schnittstellen kann von ExecutorService über die Methode submit () ausgeführt werden.
  3. Beide sind funktionale Schnittstellen und können seit Java8 in Lambda-Ausdrücken verwendet werden.

Methoden in der ExecutorService-Schnittstelle sind

<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
<T> Future<T> submit(Runnable task, T result);
Aniket Thakur
quelle
13

Zweck dieser Schnittstellen aus der Oracle-Dokumentation:

Die ausführbare Schnittstelle sollte von jeder Klasse implementiert werden, deren Instanzen von a ausgeführt werden sollen Thread. Die Klasse muss eine Methode definieren, für die keine Argumente aufgerufen werden run.

Callable : Eine Aufgabe, die ein Ergebnis zurückgibt und möglicherweise eine Ausnahme auslöst . Implementierer definieren eine einzelne Methode ohne Argumente, die als Aufruf bezeichnet werden. Die CallableSchnittstelle ist insofern ähnlich Runnable, als beide für Klassen ausgelegt sind, deren Instanzen möglicherweise von einem anderen Thread ausgeführt werden. A Runnablegibt jedoch kein Ergebnis zurück und kann keine aktivierte Ausnahme auslösen.

Andere Unterschiede:

  1. Sie können übergeben Runnable, um einen Thread zu erstellen . Sie können jedoch keinen neuen Thread erstellen, indem Sie ihn Callableals Parameter übergeben. Sie können Callable nur an ExecutorServiceInstanzen übergeben.

    Beispiel:

    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. Verwenden Sie Runnablefür Feuer und vergessen Sie Anrufe. Verwenden Sie Callablediese Option, um das Ergebnis zu überprüfen.

  3. Callablekann anders als an die invokeAll- Methode übergeben werden Runnable. Methoden invokeAnyund invokeAllführen Sie die am häufigsten verwendeten Formen der Massenausführung aus, führen Sie eine Sammlung von Aufgaben aus und warten Sie, bis mindestens eine oder alle abgeschlossen sind

  4. Trivialer Unterschied: zu implementierender Methodenname => run()für Runnableund call()für Callable.

Ravindra Babu
quelle
11

Wie hier bereits erwähnt, ist Callable eine relativ neue Schnittstelle und wurde als Teil des Parallelitätspakets eingeführt. Sowohl Callable als auch Runnable können mit Executoren verwendet werden. Class Thread (der Runnable selbst implementiert) unterstützt nur Runnable.

Sie können Runnable weiterhin mit Executoren verwenden. Der Vorteil von Callable ist, dass Sie es an den Executor senden und sofort das zukünftige Ergebnis zurückerhalten können, das aktualisiert wird, wenn die Ausführung abgeschlossen ist. Das gleiche kann mit Runnable implementiert werden, aber in diesem Fall müssen Sie die Ergebnisse selbst verwalten. Beispielsweise können Sie eine Ergebniswarteschlange erstellen, die alle Ergebnisse enthält. Andere Threads können in dieser Warteschlange warten und die eingehenden Ergebnisse verarbeiten.

AlexR
quelle
Ich frage mich, was ist das Beispiel für einen Thread, der eine Ausnahme in Java auslöst. Wird der Haupt-Thread diese Ausnahme abfangen können? Wenn nicht, würde ich Callable nicht verwenden. Alex, hast du einen Einblick dazu? Vielen Dank!
Billionen
1
Code, der wie jeder andere Code in einem benutzerdefinierten Thread ausgeführt wird, kann eine Ausnahme auslösen. Um es in einem anderen Thread zu fangen, müssen Sie einige Anstrengungen unternehmen, indem Sie entweder einen benutzerdefinierten Benachrichtigungsmechanismus (z. B. basierend auf Listenern) verwenden Futureoder einen Hook verwenden oder hinzufügen, der alle nicht erworbenen Ausnahmen abfängt: docs.oracle.com/javase/6/docs/api/ java / lang /…
AlexR
Tolle Infos! Danke, Alex! :)
Billionen
1
Ich habe diese Antwort positiv bewertet, weil sie bestätigt (korrekt, wenn sie zum Nennwert angenommen wird), dass das Thread-Pool-Modell mit aufrufbaren Objekten verwendet werden muss. Das anscheinend Unglückliche daran ist, dass man Threaddie CallableBenutzeroberfläche nicht sinnvoll nutzen kann, damit ein einzelner Thread angepasst werden kann, um aufrufbare Dinge und andere Dinge zu tun, die der Entwickler möglicherweise möchte. Wenn jemand, der diesen Kommentar liest, meint, ich
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                                                                     |
+-------------------------------------+--------------------------------------------------------------------------------------------------+

Die Entwickler von Java hatten das Bedürfnis, die Funktionen der RunnableSchnittstelle zu erweitern, aber sie wollten die Verwendung der RunnableSchnittstelle nicht beeinflussen , und wahrscheinlich war dies der Grund, warum sie sich für eine separate Schnittstelle mit dem Namen CallableJava 1.5 entschieden haben, anstatt die bereits zu ändern vorhandene RunnableSchnittstelle, die seit Java 1.0 Teil von Java ist. Quelle

Premraj
quelle
7

Der Unterschied zwischen Callable und Runnable ist folgender:

  1. Callable wird in JDK 5.0 eingeführt, Runnable wird in JDK 1.0 eingeführt
  2. Callable hat die Methode call (), aber Runnable hat die Methode run ().
  3. Callable hat eine Aufrufmethode, die einen Wert zurückgibt, aber Runnable hat eine Ausführungsmethode, die keinen Wert zurückgibt.
  4. Die Aufrufmethode kann eine geprüfte Ausnahme auslösen, die Ausführungsmethode kann jedoch keine geprüfte Ausnahme auslösen.
  5. Callable verwendet die Methode submit (), um sie in die Taskwarteschlange zu stellen, aber Runnable verwendet die Methode execute (), um sie in die Taskwarteschlange zu stellen.
Raman Gupta
quelle
Es ist wichtig zu betonen, dass diese Ausnahme aktiviert ist, nicht die RuntimeException
BertKing
5

Callable und Runnable sind einander ähnlich und können bei der Implementierung von Threads verwendet werden. Bei der Implementierung von Runnable müssen Sie die run () -Methode implementieren. Bei callable müssen Sie die call () -Methode implementieren. Beide Methoden funktionieren auf ähnliche Weise, aber die callable call () -Methode bietet mehr Flexibilität. Es gibt einige Unterschiede zwischen ihnen.

Der Unterschied zwischen Runnable und aufrufbar als below--

1) Die run () -Methode von runnable gibt void zurück . Wenn Sie möchten, dass Ihr Thread etwas zurückgibt , das Sie weiter verwenden können, haben Sie mit der runnable run () -Methode keine Wahl . Es gibt eine Lösung 'Callable' . Wenn Sie etwas in Form eines Objekts zurückgeben möchten, sollten Sie Callable anstelle von Runnable verwenden . Die aufrufbare Schnittstelle hat die Methode 'call ()', die Object zurückgibt .

Methodensignatur - Runnable->

public void run(){}

Callable->

public Object call(){}

2) Wenn bei der Runnable run () -Methode eine aktivierte Ausnahme auftritt, müssen Sie mit dem try catch-Block umgehen. Bei der Callable call () -Methode können Sie die aktivierte Ausnahme wie folgt auslösen

 public Object call() throws Exception {}

3) Runnable kommt von Legacy - Java - 1.0 - Version, aber aufrufbar kam in Java 1.5 Version mit Executer Rahmen.

Wenn Sie sind vertraut mit Vollstrecker , dann sollten Sie aufrufbare anstelle von Runnable .

Ich hoffe du verstehst.

Sudhakar Pandey
quelle
2

Runnable (vs) Callable kommt zum Tragen , wenn wir das Executer-Framework verwenden.

ExecutorService ist eine Subschnittstelle von Executor, die sowohl ausführbare als auch aufrufbare Aufgaben akzeptiert.

Ein früheres Multithreading kann über die Schnittstelle seit 1.0 erreicht werden. Hier besteht das Problem jedoch darin, dass nach Abschluss der Thread-Aufgabe die Thread-Informationen nicht erfasst werden können. Um die Daten zu sammeln, können wir statische Felder verwenden.Runnable

Beispiel Separate Threads zum Sammeln der Schülerdaten.

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

Um dieses Problem zu beheben, haben sie Since 1.5 eingeführt, das ein Ergebnis zurückgibt und möglicherweise eine Ausnahme auslöst.Callable<V>

  • Einzelne abstrakte Methode : Sowohl die Callable- als auch die Runnable-Schnittstelle verfügen über eine einzige abstrakte Methode. Dies bedeutet, dass sie in Lambda-Ausdrücken in Java 8 verwendet werden können.

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

Es gibt verschiedene Möglichkeiten, Aufgaben zur Ausführung an einen ExecutorService zu delegieren .

  • execute(Runnable task):void erstellt einen neuen Thread, blockiert jedoch nicht den Haupt-Thread oder den Aufrufer-Thread, da diese Methode void zurückgibt.
  • submit(Callable<?>):Future<?>, erstellt einen submit(Runnable):Future<?>neuen Thread und blockiert den Hauptthread, wenn Sie future.get () verwenden .

Beispiel für die Verwendung von Interfaces Runnable, Callable with Executor Framework.

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

Es ist eine Art Namenskonvention für Schnittstellen, die mit der funktionalen Programmierung übereinstimmt

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

...
yoAlex5
quelle