Antworten:
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.
Was sind die Unterschiede in den Anwendungen von
Runnable
undCallable
. Ist der Unterschied nur mit dem Rückgabeparameter in vorhandenCallable
?
Grundsätzlich ja. Siehe die Antworten auf diese Frage . Und der Javadoc fürCallable
.
Was ist die Notwendigkeit, beides zu haben, wenn
Callable
alles möglichRunnable
ist?
Weil die Runnable
Schnittstelle nicht alles kann , was sie Callable
tut!
Runnable
gibt es seit Java 1.0, wurde aber Callable
nur in Java 1.5 eingeführt ... um Anwendungsfälle zu behandeln, Runnable
die 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 Runnable
prägnanter als die Verwendung Callable<Void>
und Rückgabe eines Dummy ( null
) - Werts aus der call()
Methode.
Runnable
(weitgehend) aus Gründen der Abwärtskompatibilität existiert. Aber gibt es nicht Situationen, in denen die Implementierung (oder Anforderung) einer Callable
Schnittstelle (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?
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.)
Callable
muss implementiert call()
werden, während eine Methode Runnable
implementiert run()
werden muss.Callable
kann einen Wert zurückgeben, a Runnable
jedoch nicht.Callable
kann eine geprüfte Ausnahme auslösen, A Runnable
jedoch nicht.A Callable
kann mit ExecutorService#invokeXXX(Collection<? extends Callable<T>> tasks)
Methoden verwendet werden, a Runnable
jedoch nicht.
public interface Runnable {
void run();
}
public interface Callable<V> {
V call() throws Exception;
}
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:
Callable<V>
Instanz gibt ein Ergebnis vom Typ zurück V
, eine Runnable
Instanz jedoch nicht.Callable<V>
Instanz kann geprüfte Ausnahmen auslösen, eine Runnable
Instanz jedoch nichtDie Entwickler von Java hatten das Bedürfnis, die Funktionen der Runnable
Schnittstelle zu erweitern, aber sie wollten die Verwendung der Runnable
Schnittstelle nicht beeinflussen , und wahrscheinlich war dies der Grund, warum sie sich für eine separate Schnittstelle mit dem Namen Callable
Java 1.5 entschieden haben, anstatt die bereits zu ändern vorhanden Runnable
.
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.
Callable
Die 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;
}
Runnable
Auf 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
Runnable
Objekt gibt kein Ergebnis zurück, während ein Callable
Objekt ein Ergebnis zurückgibt.Runnable
Objekt kann keine aktivierte Ausnahme auslösen, während ein Callable
Objekt eine Ausnahme auslösen kann.Runnable
Schnittstelle gibt es seit Java 1.0, während sie Callable
nur in Java 1.5 eingeführt wurde.Einige Ähnlichkeiten schließen ein
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);
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 Callable
Schnittstelle ist insofern ähnlich Runnable
, als beide für Klassen ausgelegt sind, deren Instanzen möglicherweise von einem anderen Thread ausgeführt werden. A Runnable
gibt jedoch kein Ergebnis zurück und kann keine aktivierte Ausnahme auslösen.
Andere Unterschiede:
Sie können übergeben Runnable
, um einen Thread zu erstellen . Sie können jedoch keinen neuen Thread erstellen, indem Sie ihn Callable
als Parameter übergeben. Sie können Callable nur an ExecutorService
Instanzen übergeben.
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();
}
}
Verwenden Sie Runnable
für Feuer und vergessen Sie Anrufe. Verwenden Sie Callable
diese Option, um das Ergebnis zu überprüfen.
Callable
kann anders als an die invokeAll- Methode übergeben werden Runnable
. Methoden invokeAny
und invokeAll
fü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
Trivialer Unterschied: zu implementierender Methodenname => run()
für Runnable
und call()
für Callable
.
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.
Future
oder einen Hook verwenden oder hinzufügen, der alle nicht erworbenen Ausnahmen abfängt: docs.oracle.com/javase/6/docs/api/ java / lang /…
Thread
die Callable
Benutzeroberflä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
+-------------------------------------+--------------------------------------------------------------------------------------------------+
| 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 Runnable
Schnittstelle zu erweitern, aber sie wollten die Verwendung der Runnable
Schnittstelle nicht beeinflussen , und wahrscheinlich war dies der Grund, warum sie sich für eine separate Schnittstelle mit dem Namen Callable
Java 1.5 entschieden haben, anstatt die bereits zu ändern vorhandene Runnable
Schnittstelle, die seit Java 1.0 Teil von Java ist. Quelle
Der Unterschied zwischen Callable und Runnable ist folgender:
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.
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();
}
}
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;
}
...