Domanda

Qual è la differenza tra l'utilizzo delle interfacce Runnable e Callable quando si progetta un thread simultaneo in Java, perché dovresti sceglierne uno rispetto all'altro?

È stato utile?

Soluzione

Consulta la spiegazione qui .

  

L'interfaccia Callable è simile a   Eseguibile, in quanto entrambi sono progettati   per le classi le cui istanze sono   potenzialmente eseguito da un altro   filo. Un Runnable, tuttavia, non lo fa   restituisce un risultato e non può lanciare a   eccezione verificata.

Altri suggerimenti

  

Quali sono le differenze nelle applicazioni di Runnable e Callable. La differenza è presente solo con il parametro return in Runnable.run()?

Fondamentalmente sì. Vedi le risposte a questa domanda . E il javadoc per Callable<Void> .

  

Qual è la necessità di avere entrambi se null può fare tutto ciò che call() fa?

Perché <=> l'interfaccia non può fare tutto ciò che <=> fa!

<=> esiste da Java 1.0, ma <=> è stato introdotto solo in Java 1.5 ... per gestire casi d'uso che <=> non supporta. In teoria, il team Java avrebbe potuto modificare la firma del 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 necessita per restituire un risultato o generare un'eccezione controllata. Per questi casi d'uso, l'utilizzo di <=> è più conciso rispetto all'uso di <=> e alla restituzione di un valore fittizio (<=>) dal metodo <=>.

  • Un Callable deve implementare il metodo call() mentre un Runnable deve implementare il metodo run().
  • Un ExecutorService#invokeXXX(Collection<? extends Callable<T>> tasks) può restituire un valore ma un <=> non può.
  • Un <=> può generare un'eccezione controllata ma un <=> non può.
  • Un <=> può essere usato con i metodi <=> ma un <=> non può essere.

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

L'ho trovato in un altro blog che può spiegarlo un po 'di più questi 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'istanza Callable<V> restituisce un risultato di tipo V, mentre un'istanza Runnable no.
  • Un'istanza Callable può generare eccezioni verificate, mentre un'istanza <=> non può

I progettisti di Java sentivano il bisogno di estendere le capacità dell'interfaccia <=>, ma non volevano influenzare gli usi dell'interfaccia <=> e probabilmente questa era la ragione per cui andarono per avere un interfaccia denominata <=> in Java 1.5 rispetto alla modifica del <=>.

già esistente

Vediamo dove si userebbero 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.

Eseguibile : se si dispone di un'attività antincendio e dimentica, utilizzare Eseguibile. 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 esegui il tuo compito.

Callable : se stai cercando di recuperare un valore da un'attività, usa 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 avvolti 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 di 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 Richiama racchiuso in Future, dovrà bloccare 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.

L'interfaccia

Callable dichiara il metodo call() ed è necessario fornire generici poiché il tipo di chiamata Object () 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;
}

Runnable d'altra parte è l'interfaccia che dichiara il metodo run() 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();
}

Per riassumere alcune notevoli differenze sono

  1. Un oggetto <=> non restituisce un risultato mentre un <=> oggetto restituisce un risultato.
  2. Un oggetto <=> non può generare un'eccezione controllata laddove un oggetto <=> può lanciare un oggetto fa eccezione.
  3. L'interfaccia <=> è disponibile da Java 1.0 mentre <=> è stato solo introdotto in Java 1.5.

Alcune somiglianze includono

  1. Le istanze delle classi che implementano interfacce Runnable o Callable sono potenzialmente eseguito 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 ExecutorService sono

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

Scopo di queste interfacce dalla documentazione di Oracle:

L'interfaccia

Runnable deve essere implementata da qualsiasi classe le cui istanze devono essere eseguite da un Thread. La classe deve definire un metodo senza argomenti chiamato run.

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

Altre differenze:

  1. Puoi passare invokeAny per creare un thread . Ma non puoi creare un nuovo thread passando invokeAll come parametro. Puoi trasferire Callable solo a run() istanze.

    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. Usa call() per sparare e dimenticare le chiamate. Usa <=> per verificare il risultato.

  3. <=> può essere passato a invokeAll metodo diversamente da <=>. I metodi <=> e <=> eseguono le 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 = > <=> per <=> e <=> per <=>.

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.

+-------------------------------------+--------------------------------------------------------------------------------------------------+
|              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à dell'interfaccia Runnable, ma non volevano influenzare gli usi dell'interfaccia Callable e probabilmente questa era la ragione per cui andarono per avere un interfaccia denominata <=> in Java 1.5 rispetto alla modifica dell'interfaccia <=> già esistente che fa parte di Java da Java 1.0. source

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à.

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

Differenza tra eseguibile e richiamabile come di seguito--

1) Il metodo run () di eseguibile restituisce vuoto , significa che se vuoi che il tuo thread restituisca qualcosa che puoi usare ulteriormente non hai nessuna scelta con il metodo Runnable run () . Esiste una soluzione 'Callable' , se vuoi restituire qualsiasi cosa in forma di oggetto , allora dovresti usare Callable anziché Runnable . L'interfaccia richiamabile ha il metodo 'call ()' che restituisce Object .

Firma del metodo - Runnable - gt &;

public void run(){}
& -

Callable gt;

public Object call(){}

2) Nel caso del metodo Runnable run () se si verifica un'eccezione verificata, è necessario gestire con il blocco catch try , ma in caso di Chiamata richiamabile () che puoi generare un'eccezione controllata come di seguito

 public Object call() throws Exception {}

3) Eseguibile proviene dalla versione precedente java 1.0 , ma richiamabile è arrivato in Java 1.5 con < strong> Executer framework.

Se hai familiarità con Executers , dovresti utilizzare Callable anziché Runnable .

Spero che tu capisca.

Runnable (vs) Callable è 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 l'interfaccia Runnable da 1.0 , ma qui il problema è dopo aver completato l'attività di thread che non sono in grado di raccogliere le informazioni sui thread. Al fine di raccogliere i dati possiamo utilizzare i campi statici.

Esempio di thread separati 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 Callable<V> Dall'1.5 che restituisce un risultato e può generare un'eccezione.

  • Metodo astratto singolo : l'interfaccia Callable e Runnable hanno un unico 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 null.
  • submit(Callable<?>):Future<?>, submit(Runnable):Future<?> crea un 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();
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top