Domanda "Implements Runnable" vs. "extends Thread"


Da che ora ho trascorso con i thread in Java, ho trovato questi due modi per scrivere thread:

Con implements Runnable:

public class MyRunnable implements Runnable {
    public void run() {
        //Code
    }
}
//Started with a "new Thread(new MyRunnable()).start()" call

O con extends Thread:

public class MyThread extends Thread {
    public MyThread() {
        super("MyThread");
    }
    public void run() {
        //Code
    }
}
//Started with a "new MyThread().start()" call

C'è qualche differenza significativa in questi due blocchi di codice?


1793
2018-02-12 14:28


origine


risposte:


Sì: attrezzi Runnable è il modo preferito per farlo, IMO. Non stai davvero specializzando il comportamento del thread. Stai solo dando qualcosa da eseguire. Questo significa composizione è il filosoficamente "più puro" modo di andare.

In pratico termini, significa che puoi implementare Runnable ed estendersi anche da un'altra classe.


1445
2018-02-12 14:32



tl; dr: implements Runnable è migliore. Tuttavia, l'avvertenza è importante

In generale, consiglierei di usare qualcosa di simile Runnable piuttosto che Thread perché ti consente di mantenere il tuo lavoro solo in modo approssimativo con la tua scelta di concorrenza. Ad esempio, se si utilizza a Runnable e deciderà più tardi che ciò non richiede di fatto il suo Thread, puoi semplicemente chiamare threadA.run ().

Avvertimento: Da queste parti scoraggia fortemente l'uso di Thread non elaborati. Preferisco di gran lunga l'uso di callable e FutureTasks (Da javadoc: "Un calcolo asincrono cancellabile"). L'integrazione di timeout, cancellazione appropriata e il pooling di thread del moderno supporto di concorrenza sono molto più utili per me di pile di thread non elaborati.

Azione supplementare: c'è un FutureTask costruttore che ti permette di usare Runnables (se è quello che ti piace di più) e ottenere comunque il beneficio dei moderni strumenti di concorrenza. Per citare il javadoc:

Se non hai bisogno di un risultato particolare, considera l'utilizzo di costruzioni del modulo:

Future<?> f = new FutureTask<Object>(runnable, null)

Quindi, se sostituiamo il loro runnable con il tuo threadA, otteniamo quanto segue:

new FutureTask<Object>(threadA, null)

Un'altra opzione che ti permette di stare più vicino a Runnables è a ThreadPoolExecutor. Puoi usare il eseguire metodo per passare un Runnable per eseguire "l'attività assegnata in futuro".

Se desideri provare a utilizzare un pool di thread, il frammento di codice sopra riportato diventerà simile al seguente (utilizzando il comando Executors.newCachedThreadPool () metodo di fabbrica):

ExecutorService es = Executors.newCachedThreadPool();
es.execute(new ThreadA());

494
2018-02-12 14:37



Morale della storia:

Eredita solo se vuoi sovrascrivere un comportamento.

O meglio dovrebbe essere letto come:

Eredita di meno, interfaccia di più.


225
2018-03-11 15:50



Bene, tante buone risposte, voglio aggiungere altro su questo. Questo aiuterà a capire Extending v/s Implementing Thread.
Extends lega molto strettamente due file di classe e può causare alcuni problemi con il codice.

Entrambi gli approcci fanno lo stesso lavoro ma ci sono state alcune differenze.
La differenza più comune è 

  1. Quando si estende la classe Thread, dopo di che non è possibile estendere qualsiasi altra classe richiesta. (Come sai, Java non consente di ereditare più di una classe).
  2. Quando implementi Runnable, puoi salvare uno spazio affinché la tua classe estenda qualsiasi altra classe in futuro o ora.

Tuttavia, uno differenza significativa tra l'implementazione di Thread eseguibile ed esteso è quello
  by extending Thread, each of your threads has a unique object associated with it, whereas implementing Runnable, many threads can share the same object instance.

Il seguente esempio ti aiuta a capire più chiaramente

//Implement Runnable Interface...
 class ImplementsRunnable implements Runnable {

private int counter = 0;

public void run() {
    counter++;
    System.out.println("ImplementsRunnable : Counter : " + counter);
 }
}

//Extend Thread class...
class ExtendsThread extends Thread {

private int counter = 0;

public void run() {
    counter++;
    System.out.println("ExtendsThread : Counter : " + counter);
 }
}

//Use above classes here in main to understand the differences more clearly...
public class ThreadVsRunnable {

public static void main(String args[]) throws Exception {
    // Multiple threads share the same object.
    ImplementsRunnable rc = new ImplementsRunnable();
    Thread t1 = new Thread(rc);
    t1.start();
    Thread.sleep(1000); // Waiting for 1 second before starting next thread
    Thread t2 = new Thread(rc);
    t2.start();
    Thread.sleep(1000); // Waiting for 1 second before starting next thread
    Thread t3 = new Thread(rc);
    t3.start();

    // Creating new instance for every thread access.
    ExtendsThread tc1 = new ExtendsThread();
    tc1.start();
    Thread.sleep(1000); // Waiting for 1 second before starting next thread
    ExtendsThread tc2 = new ExtendsThread();
    tc2.start();
    Thread.sleep(1000); // Waiting for 1 second before starting next thread
    ExtendsThread tc3 = new ExtendsThread();
    tc3.start();
 }
}

Uscita del programma di cui sopra.

ImplementsRunnable : Counter : 1
ImplementsRunnable : Counter : 2
ImplementsRunnable : Counter : 3
ExtendsThread : Counter : 1
ExtendsThread : Counter : 1
ExtendsThread : Counter : 1

Nell'approccio Runnable interface, viene creata una sola istanza di una classe ed è stata condivisa da thread diversi. Quindi il valore del contatore viene incrementato per ogni accesso al thread.

Considerando che, l'approccio della classe Thread, è necessario creare un'istanza separata per ogni accesso al thread. Quindi la memoria diversa è allocata per ogni istanza di classe e ciascuno ha un contatore separato, il valore rimane lo stesso, il che significa che non si verificherà alcun incremento perché nessuno del riferimento all'oggetto è lo stesso.

Quando usare Runnable?
Utilizzare l'interfaccia Runnable quando si desidera accedere alla stessa risorsa dal gruppo di thread. Evita di usare la classe Thread qui, perché la creazione di più oggetti consuma più memoria e diventa un grande overhead delle prestazioni.

Una classe che implementa Runnable non è un thread e solo una classe. Affinché un Runnable diventi un thread, è necessario creare un'istanza di Thread e passarsi come target.

Nella maggior parte dei casi, l'interfaccia Runnable dovrebbe essere utilizzata se si prevede di ignorare solo l'interfaccia run() metodo e nessun altro metodo Thread. Questo è importante perché le classi non dovrebbero essere sottoclassate a meno che il programmatore non intenda modificare o migliorare il comportamento fondamentale della classe.

Quando è necessario estendere una superclasse, l'implementazione dell'interfaccia Runnable è più appropriata rispetto all'utilizzo della classe Thread. Perché possiamo estendere un'altra classe mentre implementiamo l'interfaccia Runnable per creare un thread.

Spero che questo ti possa aiutare!


183
2018-03-05 14:26



Una cosa che mi sorprende non è stata ancora menzionata è l'implementazione Runnable rende la tua classe più flessibile.

Se estendi il thread, l'azione che stai facendo sarà sempre in una discussione. Tuttavia, se si implementa Runnable non deve essere. È possibile eseguirlo in un thread o passarlo a qualche tipo di servizio executor, o semplicemente passarlo come attività all'interno di una singola applicazione a thread (magari per essere eseguito in un secondo momento, ma all'interno dello stesso thread). Le opzioni sono molto più aperte se la usi Runnable che se ti leghi a Thread.


73
2018-02-12 14:51



Se vuoi implementare o estendere qualsiasi altra classe allora Runnable l'interfaccia è più preferibile se non si desidera che qualsiasi altra classe venga estesa o implementata Thread la classe è preferibile

La differenza più comune è

enter image description here

Quando tu extends Thread classe, dopo di che non è possibile estendere qualsiasi altra classe richiesta. (Come sai, Java non consente di ereditare più di una classe).

Quando tu implements Runnable, puoi salvare uno spazio per la tua classe per estendere qualsiasi altra classe in futuro o ora.

  • Java non supporta l'ereditarietà multipla, il che significa che puoi estendere una sola classe in Java, quindi una volta estesa la classe Thread hai perso le possibilità e non puoi estendere o ereditare un'altra classe in Java.

  • Nella programmazione orientata agli oggetti estendere una classe in genere significa aggiungere nuove funzionalità, modificare o migliorare i comportamenti. Se non apportiamo alcuna modifica a Thread, utilizza invece l'interfaccia Runnable.

  • L'interfaccia eseguibile rappresenta un'attività che può essere eseguita sia da Thread normale che da Executor o qualsiasi altro mezzo. la separazione logica di Task come Runnable che Thread è una buona decisione di progettazione.

  • Separare il compito come Runnable significa che possiamo riutilizzare il compito e avere anche la libertà di eseguirlo da diversi mezzi. dal momento che non è possibile riavviare una discussione una volta completata. ancora Runnable vs Thread per task, Runnable è vincitore.

  • Designer Java riconosce questo ed è per questo che gli esecutori accettano Runnable as Task e hanno thread di lavoro che esegue tali attività.

  • Ereditare tutti i metodi Thread è un overhead aggiuntivo solo per rappresentare un'attività che può essere eseguita facilmente con Runnable.

Per gentile concessione di javarevisited.blogspot.com

Queste erano alcune delle notevoli differenze tra Thread e Runnable in Java, se si conoscono altre differenze su Thread vs Runnable, si prega di condividerle tramite commenti. Personalmente utilizzo Runnable su Thread per questo scenario e mi consiglia di utilizzare l'interfaccia Runnable o Callable in base alle tue esigenze.

Tuttavia, la differenza significativa è.

Quando tu extends Thread classe, ogni thread crea un oggetto unico e si associa ad esso. Quando tu implements Runnable, condivide lo stesso oggetto su più thread.


65
2018-05-11 08:59



In realtà, non è saggio confrontare Runnable e Thread insieme.

Questi due hanno una dipendenza e una relazione in multi-threading proprio come Wheel and Engine relazione dell'autoveicolo.

Direi, esiste un solo modo per il multi-threading con due passaggi. Lasciatemi esprimere il mio punto.

Runnable:
Quando si implementa interface Runnable significa che stai creando qualcosa che è run able in un thread diverso. Ora creare qualcosa che può essere eseguito all'interno di un thread (eseguibile in un thread), non significa creare un Thread.
Quindi la classe MyRunnable non è altro che una classe ordinaria con a void run metodo. E gli oggetti saranno oggetti ordinari con solo un metodo run che verrà eseguito normalmente quando chiamato. (a meno che non passiamo l'oggetto in un thread).

Filo:
class Thread, Direi Una classe molto speciale con la capacità di avviare una nuova discussione che consente effettivamente la multi-threading attraverso la sua start() metodo.

Perché non saggio da confrontare?
Perché abbiamo bisogno di entrambi per il multi-threading.

Per il multi-threading abbiamo bisogno di due cose:

  • Qualcosa che può essere eseguito all'interno di un thread (eseguibile).
  • Qualcosa che può iniziare una nuova discussione (discussione).

Quindi tecnicamente e teoricamente entrambi sono necessari per iniziare un thread, uno lo farà correre e uno sarà fallo correre (Piace Wheel and Engine dell'autoveicolo).

Ecco perché non puoi iniziare un thread con MyRunnable è necessario passarlo a un'istanza di Thread.

Ma è possibile creare ed eseguire un thread usando solo class Thread perché Class Thread attrezzi Runnable quindi lo sappiamo tutti Thread inoltre è un Runnable dentro.

Finalmente Thread e Runnable sono complementari l'un l'altro per il multithreading non concorrente o sostitutivo.


60
2018-05-12 13:50



Dovresti implementare Runnable, ma se stai usando Java 5 o versioni successive, non dovresti avviarlo con new Thread ma usa un ExecutorService anziché. Per dettagli vedi: Come implementare il threading semplice in Java.


41
2018-02-12 14:41



Non sono un esperto, ma posso pensare a un motivo per implementare Runnable anziché estendere Thread: Java supporta solo l'ereditarietà singola, quindi puoi estendere solo una classe.

Modifica: originariamente detto "L'implementazione di un'interfaccia richiede meno risorse". pure, ma è necessario creare una nuova istanza di Thread in entrambi i casi, quindi questo era sbagliato.


31
2018-02-12 14:32



Direi che c'è una terza via:

public class Something {

    public void justAnotherMethod() { ... }

}

new Thread(new Runnable() {
   public void run() {
    instanceOfSomething.justAnotherMethod();
   }
}).start();

Forse questo è influenzato un po 'dal mio recente uso pesante di Javascript e Actionscript 3, ma in questo modo la tua classe non ha bisogno di implementare un'interfaccia piuttosto vaga come Runnable.


19
2017-10-25 21:41



Con il rilascio di Java 8, ora c'è una terza opzione.

Runnable è un interfaccia funzionale, il che significa che le sue istanze possono essere create con espressioni lambda o riferimenti al metodo.

Il tuo esempio può essere sostituito con:

new Thread(() -> { /* Code here */ }).start()

o se vuoi usare un ExecutorService e un riferimento al metodo:

executor.execute(runner::run)

Questi non sono solo molto più brevi dei tuoi esempi, ma hanno anche molti dei vantaggi dichiarati in altre risposte sull'uso Runnable al di sopra di Thread, come la responsabilità singola e l'uso della composizione perché non stai specializzando il comportamento del thread. In questo modo evita anche di creare una classe extra se tutto ciò di cui hai bisogno è a Runnable come fai nei tuoi esempi.


16
2017-07-29 11:24