La differenza tra le interfacce Runnable e Callable in Java


Risposte:


444

Vedi la spiegazione qui .

L'interfaccia Callable è simile a Runnable, in quanto entrambi sono progettati per classi le cui istanze sono potenzialmente eseguite da un altro thread. Un Runnable, tuttavia, non restituisce un risultato e non può generare un'eccezione controllata.


270

Quali sono le differenze nelle applicazioni di Runnablee Callable. La differenza è presente solo con il parametro return in Callable?

Fondamentalmente sì. Vedi le risposte a questa domanda . E il javadoc perCallable .

Qual è la necessità di avere entrambi se Callablepuò fare tutto ciò che Runnablefa?

Perché l' Runnableinterfaccia non può fare tutto ciò che Callablefa!

Runnableesiste da Java 1.0, ma è Callablestato introdotto solo in Java 1.5 ... per gestire casi d'uso che Runnablenon supportano. In teoria, il team Java avrebbe potuto modificare la firma del Runnable.run()metodo, ma ciò avrebbe compromesso la compatibilità binaria con il codice pre-1.5, richiedendo la ricodifica durante la migrazione del vecchio codice Java alle nuove JVM. Questo è un GRANDE NO-NO. Java si sforza di essere retrocompatibile ... ed è stato uno dei maggiori punti di forza di Java per il business computing.

E, ovviamente, ci sono casi d'uso in cui un'attività non deve restituire un risultato o generare un'eccezione controllata. Per questi casi d'uso, l'utilizzo Runnableè più conciso dell'uso Callable<Void>e della restituzione di un nullvalore dummy ( ) dal call()metodo.


9
Mi chiedo da dove hai preso questa storia. Questo è molto utile
Spiderman,

4
@prash: i fatti di base si trovano nei vecchi libri di testo. Come la prima edizione di Java in a Nutshell.
Stephen C,

4
(@prash - Anche ... iniziando a usare Java nell'era Java 1.1.)
Stephen C

1
@StephenC Se ho letto correttamente la tua risposta, stai suggerendo che Runnableesiste (in gran parte) per motivi di compatibilità con le versioni precedenti. Ma non ci sono situazioni in cui è inutile o troppo costoso implementare (o richiedere) l' Callableinterfaccia (ad es., In ScheduledFuture<?> ScheduledExecutorService.schedule(Runnable command, long delay, TimeUnit unit))? Quindi non c'è alcun vantaggio nel mantenere entrambe le interfacce nella lingua, anche se la storia non ha forzato il risultato attuale?
max

1
@max - Beh, l'ho detto e sono ancora d'accordo. Tuttavia, questo è un motivo secondario. Ma anche così, sospetto che Runnable sarebbe stato modificato se non ci fosse stato un imperativo per mantenere la compatibilità. La "caldaia" di return null;è un argomento debole. (Almeno, quella sarebbe stata la mia decisione ... nell'ipotetico contesto in cui potresti ignorare la retrocompatibilità.)
Stephen C

82
  • È Callablenecessario implementare il call()metodo mentre è Runnablenecessario implementare il run()metodo.
  • A Callablepuò restituire un valore ma Runnablenon può.
  • A Callablepuò generare un'eccezione controllata ma Runnablenon è possibile.
  • A Callablepuò essere usato con ExecutorService#invokeXXX(Collection<? extends Callable<T>> tasks)metodi ma Runnablenon può essere.

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

17
ExecutorService.submit (attività eseguibile) esiste anche ed è molto utile
Yair Kukielka,

Runnable può anche essere usato con ExecutorService seguendo i modi- 1) ExecutorService.execute (Runnable) 2) ExecutorService.submit (Runnable)
Azam Khan

2
Inoltre è presente Executor.submit (attività Callable <T>) ma non è possibile richiamare All o invokeAny con la raccolta di attività Runnable Collection <? estende le attività <T>> richiamabili
nikli

36

Ho trovato questo in un altro blog che può spiegarlo un po 'di più queste differenze :

Sebbene entrambe le interfacce siano implementate dalle classi che desiderano eseguire in un diverso thread di esecuzione, ma ci sono alcune differenze tra le due interfacce che sono:

  • Un Callable<V>esempio restituisce un risultato di tipo V, mentre Runnableun'istanza non lo fa.
  • Un Callable<V>esempio può lanciare eccezioni controllate, mentre Runnableun'istanza non può

I progettisti di Java sentivano il bisogno di estendere le capacità Runnabledell'interfaccia, ma non volevano influenzare gli usi Runnabledell'interfaccia e probabilmente questo era il motivo per cui avevano scelto un'interfaccia separata chiamata Callablein Java 1.5 piuttosto che cambiare il già esistente Runnable.


27

Vediamo dove si potrebbero usare Runnable e Callable.

Runnable e Callable vengono entrambi eseguiti su un thread diverso rispetto al thread chiamante. Ma Callable può restituire un valore e Runnable non può. Quindi, dove si applica davvero questo.

Runnable : se hai un incendio e dimentica l'attività, usa Runnable. Inserisci il tuo codice in un Runnable e quando viene chiamato il metodo run (), puoi eseguire la tua attività. Al thread chiamante non importa davvero quando si esegue l'attività.

Callable : se si sta tentando di recuperare un valore da un'attività, utilizzare Callable. Ora richiamabile da solo non farà il lavoro. Avrai bisogno di un futuro che avvolgi il tuo Callable e ottenga i tuoi valori su future.get (). Qui il thread chiamante verrà bloccato fino a quando il Future non tornerà con risultati che a loro volta attendono l'esecuzione del metodo call () di Callable.

Pensa quindi a un'interfaccia per una classe di destinazione in cui sono definiti sia i metodi di Runnable che Callable. La classe chiamante chiamerà casualmente i tuoi metodi di interfaccia senza sapere quale sia Runnable e quale sia Callable. I metodi Runnable verranno eseguiti in modo asincrono, fino a quando viene chiamato un metodo Callable. Qui il thread della classe chiamante verrà bloccato poiché stai recuperando valori dalla tua classe target.

NOTA: all'interno della classe target è possibile effettuare chiamate a Callable e Runnable su un singolo esecutore thread, rendendo questo meccanismo simile a una coda di invio seriale. Quindi, fintanto che il chiamante chiama i tuoi metodi avvolti Runnable, il thread chiamante verrà eseguito molto velocemente senza bloccarsi. Non appena chiamerà un metodo chiamato nel metodo Future, dovrà bloccarsi fino a quando tutti gli altri elementi in coda non verranno eseguiti. Solo allora il metodo tornerà con i valori. Questo è un meccanismo di sincronizzazione.


14

Callablel'interfaccia dichiara il call()metodo ed è necessario fornire generici poiché il tipo di Object call () dovrebbe restituire -

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'altra parte è l'interfaccia che dichiara il run()metodo che viene chiamato quando si crea un thread con il runnable e si chiama start () su di esso. Puoi anche chiamare direttamente run () ma che esegue semplicemente il metodo run () è lo stesso 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();
}

Riassumendo alcune notevoli differenze sono

  1. Un Runnableoggetto non restituisce un risultato mentre un Callableoggetto restituisce un risultato.
  2. Un Runnableoggetto non può generare un'eccezione controllata laddove un Callableoggetto può generare un'eccezione.
  3. L' Runnableinterfaccia esiste da Java 1.0 mentre è Callablestata introdotta solo in Java 1.5.

Poche somiglianze includono

  1. Le istanze delle classi che implementano interfacce Runnable o Callable sono potenzialmente eseguite da un altro thread.
  2. L'istanza di entrambe le interfacce Callable e Runnable può essere eseguita da ExecutorService tramite il metodo submit ().
  3. Entrambe sono interfacce funzionali e possono essere utilizzate nelle espressioni Lambda da Java8.

I metodi nell'interfaccia di ExecutorService sono

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

13

Scopo di queste interfacce dalla documentazione di Oracle:

L' interfaccia eseguibile deve essere implementata da qualsiasi classe le cui istanze devono essere eseguite da a Thread. La classe deve definire un metodo senza argomenti chiamati run.

Callable : un'attività che restituisce un risultato e può generare un'eccezione. Gli implementatori definiscono un singolo metodo senza argomenti chiamati call. L' Callableinterfaccia è simile a Runnablequella in quanto entrambi sono progettati per classi le cui istanze sono potenzialmente eseguite da un altro thread. A Runnable, tuttavia, non restituisce un risultato e non può generare un'eccezione controllata.

Altre differenze:

  1. Puoi passare Runnableper creare una discussione . Ma non puoi creare un nuovo thread passando Callablecome parametro. È possibile passare a Callable solo alle ExecutorServiceistanze.

    Esempio:

    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. Utilizzare Runnableper il fuoco e dimenticare le chiamate. Utilizzare Callableper verificare il risultato.

  3. Callablepuò essere passato al metodo invokeAll diversamente dal Runnable. Metodi invokeAnyed invokeAllesecuzione delle forme più comunemente utili di esecuzione in blocco, eseguendo una raccolta di attività e quindi aspettando che almeno una o tutte le operazioni vengano completate

  4. Differenza fondamentale: nome del metodo da implementare => run()per Runnablee call()per Callable.


11

Come già menzionato qui, Callable è un'interfaccia relativamente nuova ed è stata introdotta come parte del pacchetto di concorrenza. Sia Callable che Runnable possono essere usati con gli esecutori. Il thread di classe (che implementa Runnable stesso) supporta solo Runnable.

Puoi ancora usare Runnable con gli esecutori. Il vantaggio di Callable è che puoi inviarlo all'esecutore e ottenere immediatamente il risultato futuro che verrà aggiornato al termine dell'esecuzione. Lo stesso può essere implementato con Runnable, ma in questo caso devi gestire i risultati da solo. Ad esempio è possibile creare una coda di risultati che conterrà tutti i risultati. Altre discussioni possono attendere su questa coda e gestire i risultati che arrivano.


mi chiedo quale sia l'esempio su un thread che genera un'eccezione in Java? il thread principale sarà in grado di catturare quell'eccezione? Altrimenti, non userei Callable. Alex, hai qualche idea su questo? Grazie!
trilioni il

1
Il codice in esecuzione nel thread personalizzato poiché qualsiasi altro codice può generare eccezioni. Per catturarlo in un altro thread devi compiere alcuni sforzi utilizzando il meccanismo di notifica personalizzato (ad esempio basato su listener) o utilizzando Futureo aggiungendo un hook che rileva tutte le eccezioni non rilevate
AlexR

Informazioni fantastiche! Grazie Alex! :)
trilioni il

1
Ho votato a favore di questa risposta perché afferma (correttamente se preso al valore nominale) si deve usare il modello di pool di thread con oggetti richiamabili. La cosa apparentemente sfortunata in questo è che non si può estendere Threada fare un uso significativo Callabledell'interfaccia in modo che un singolo thread possa essere personalizzato per fare cose richiamabili e altre cose che lo sviluppatore potrebbe desiderare. Se qualcuno che legge questo commento pensa che mi sbaglio, mi piacerebbe saperlo meglio ...

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

I progettisti di Java sentivano il bisogno di estendere le capacità Runnabledell'interfaccia, ma non volevano influenzare gli usi Runnabledell'interfaccia e probabilmente questo era il motivo per cui avevano scelto un'interfaccia separata chiamata Callablein Java 1.5 piuttosto che cambiare il già Runnableinterfaccia esistente che fa parte di Java da Java 1.0. fonte


7

Le differenze tra Callable e Runnable sono le seguenti:

  1. Callable è stato introdotto in JDK 5.0 ma Runnable è stato introdotto in JDK 1.0
  2. Callable ha il metodo call () ma Runnable ha il metodo run ().
  3. Callable ha un metodo call che restituisce valore ma Runnable ha un metodo run che non restituisce alcun valore.
  4. il metodo call può generare un'eccezione controllata ma il metodo run non può generare un'eccezione controllata.
  5. Callable utilizza il metodo submit () per inserire la coda delle attività, ma Runnable usa il metodo execute () per inserire la coda delle attività.

È importante sottolineare che ha verificato l'eccezione , non RuntimeException
BertKing,

5

Callable e Runnable sono entrambi simili tra loro e possono essere utilizzati nell'implementazione del thread. In caso di implementazione di Runnable è necessario implementare il metodo run () ma in caso di chiamata è necessario implementare il metodo call () , entrambi i metodi funzionano in modo simile ma il metodo call () ha una maggiore flessibilità. Esistono alcune differenze.

Differenza tra eseguibile e richiamabile come di seguito--

1) Il metodo run () di runnable restituisce void , significa che se vuoi che il tuo thread restituisca qualcosa che puoi usare ulteriormente, non hai scelta con il metodo run () Runnable . Esiste una soluzione "Callable" , se si desidera restituire qualsiasi cosa in forma di oggetto, è necessario utilizzare Callable anziché Runnable . L'interfaccia richiamabile ha il metodo 'call ()' che restituisce Object .

Firma del metodo - Eseguibile->

public void run(){}

Callable->

public Object call(){}

2) Nel caso del metodo Run () Runnable se si verifica un'eccezione verificata, è necessario gestirla con try catch block , ma nel caso del metodo Call () Callable è possibile generare l'eccezione controllata come di seguito

 public Object call() throws Exception {}

3) Runnable proviene dalla versione java 1.0 legacy , ma callable è arrivato in versione Java 1.5 con framework Executer .

Se hai familiarità con gli Executer , dovresti usare Callable anziché Runnable .

Spero tu capisca.


2

Runnable (vs) Callable diventa importante quando stiamo usando Executer framework.

ExecutorService è una sottointerfaccia di Executor, che accetta attività eseguibili e richiamabili.

Il multi-threading precedente può essere ottenuto utilizzando Interface Since 1.0 , ma qui il problema è dopo aver completato l'attività thread non siamo in grado di raccogliere le informazioni sui thread. Al fine di raccogliere i dati possiamo utilizzare i campi statici.Runnable

Esempio Discussioni separate per raccogliere i dati di ogni studente.

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

Per risolvere questo problema hanno introdotto da 1.5 che restituisce un risultato e può generare un'eccezione.Callable<V>

  • Metodo astratto singolo : Sia l'interfaccia Callable che Runnable hanno un singolo metodo astratto, il che significa che possono essere utilizzati nelle espressioni lambda in java 8.

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

Esistono diversi modi per delegare le attività per l'esecuzione a un ExecutorService .

  • execute(Runnable task):void crea un nuovo thread ma non blocca il thread principale o il thread del chiamante poiché questo metodo restituisce void.
  • submit(Callable<?>):Future<?>, submit(Runnable):Future<?>crea nuovo thread e blocca il thread principale quando si utilizza future.get () .

Esempio di utilizzo di interfacce eseguibili, richiamabili con 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

È una specie di convenzione di denominazione dell'interfaccia che si abbina alla programmazione funzionale

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

...
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.