"Implementa Runnable" vs "estende Thread" in Java


2119

Da quanto tempo 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

Oppure, 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?


57
Grazie per questa domanda, le risposte hanno chiarito molte idee sbagliate che avevo. Ho esaminato il modo corretto di eseguire i thread Java prima che esistesse SO e c'erano molte informazioni errate / non aggiornate là fuori.
James McMahon,

5
c'è un motivo per cui potresti voler estendere Thread ( ma non lo consiglio ), puoi gestirlo preventivamente interrupt(). Ancora una volta, è un'idea, potrebbe essere utile nel caso giusto, tuttavia non lo consiglio.
bestsss,

Si prega di consultare anche la risposta, ben spiegato: stackoverflow.com/q/5562720/285594

@bestsss, sto cercando di capire cosa potresti voler dire sulla gestione di interrupt (). Stai cercando di ignorare il metodo?
Bob Cross,

8
Sì. Secondo il codice, la classe Thread A può estendere qualsiasi classe mentre la classe Thread B non può estendere qualsiasi altra classe
mani deepak

Risposte:


1673

Sì: implementa Runnableè il modo preferito per farlo, IMO. Non stai davvero specializzando il comportamento del thread. Gli stai solo dando qualcosa per correre. Ciò significa che la composizione è filosoficamente strada "più pura" da percorrere.

In termini pratici , significa che puoi implementare Runnableed estendere anche da un'altra classe.


151
Esatto, ben messo. Quale comportamento stiamo cercando di sovrascrivere in Thread estendendolo? Direi che la maggior parte delle persone non sta cercando di sovrascrivere alcun comportamento, ma sta cercando di usare il comportamento di Thread.
hooknc

87
Come commento laterale, se si crea un'istanza di un thread e non si chiama il suo metodo start (), si sta creando una perdita di memoria in Java <5 (ciò non accade con Runnables): stackoverflow.com/questions/107823/…
Nacho Coloma

25
Un vantaggio minore di Runnable è che, se in determinate circostanze non ti interessa o non vuoi usare il threading e vuoi semplicemente eseguire il codice, hai la possibilità di chiamare semplicemente run (). ad es. (molto manuale) if (numberCores > 4) myExecutor.excute(myRunnable); else myRunnable.run()
user949300

10
@ user949300 puoi anche farlo con extends Threade se non vuoi il threading perché dovresti persino implementare Runnable...
m0skit0

30
Per parafrasare Sierra e Bates, un vantaggio chiave dell'implementazione di Runnable è che stai separando architettonicamente il "lavoro" dal "corridore".
8bitjunkie,

569

tl; dr: implementa Runnable è meglio. Tuttavia, l'avvertenza è importante

In generale, consiglierei di usare qualcosa di simile Runnablepiuttosto che Threadperché ti consente di mantenere il tuo lavoro solo vagamente accoppiato con la tua scelta di concorrenza. Ad esempio, se si utilizza a Runnablee si decide in seguito che ciò non richiede effettivamente il proprioThread , puoi semplicemente chiamare threadA.run ().

Avvertenza: qui intorno, scoraggio fortemente l'uso di thread grezzi. Preferisco di gran lunga l'uso di Callables e FutureTasks (dal javadoc: "Un calcolo asincrono cancellabile"). L'integrazione di timeout, la corretta cancellazione e il pool di thread del supporto di concorrenza moderno mi sono molto più utili delle pile di thread grezzi.

Follow-up: esiste un FutureTaskcostruttore che ti consente di utilizzare Runnables (se è quello che ti fa sentire più a tuo agio) e di ottenere comunque i vantaggi 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 lo sostituiamo runnablecon il tuo threadA, otteniamo quanto segue:

new FutureTask<Object>(threadA, null)

Un'altra opzione che ti consente di stare più vicino a Runnables è un ThreadPoolExecutor . È possibile utilizzare il metodo execute per passare un Runnable per eseguire "l'attività data in futuro".

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

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

40
Questo è meglio della risposta accettata IMHO. Una cosa: lo snippet di codice che hai non chiude l'esecutore e vedo milioni di domande in cui le persone sbagliano, creando un nuovo Executor ogni volta che vogliono generare un'attività. essarebbe meglio come campo statico (o iniettato) in modo che venga creato una sola volta.
artbristol,

7
@artbristol, grazie! Non sono in disaccordo con il nuovo Executor (facciamo ciò che suggerisci nel nostro codice). Nello scrivere la risposta originale, stavo cercando di scrivere un codice minimo analogo al frammento originale. Dobbiamo sperare che molti lettori di queste risposte li usino come punti saltanti. Non sto cercando di scrivere un sostituto per il javadoc. Sto effettivamente scrivendo materiale di marketing per questo: se ti piace questo metodo, dovresti vedere tutte le altre grandi cose che abbiamo da offrire ...!
Bob Cross,

3
So di essere un po 'in ritardo a commentare questo, ma trattare FutureTaskdirettamente non è generalmente quello che vuoi fare. ExecutorServices creerà l'appropriato Futureper voi quando si submitun Runnable/ Callablea loro. Allo stesso modo per ScheduledExecutorServices e ScheduledFuturequando scheduleun Runnable/ Callable.
Powerlord,

3
@Powerlord, la mia intenzione era quella di creare frammenti di codice che corrispondessero il più possibile agli OP. Sono d'accordo che il nuovo FutureTask non è ottimale ma è chiaro ai fini della spiegazione.
Bob Cross,

257

Morale della storia:

Eredita solo se vuoi ignorare alcuni comportamenti.

O meglio, dovrebbe essere letto come:

Eredita di meno, interfaccia di più.


1
Questa dovrebbe essere sempre la domanda se inizi a creare un oggetto in esecuzione simultanea! Ti servono anche le funzioni Discussione oggetto?
Liebertee,

4
Quando si eredita da Thread, si vuole quasi sempre ignorare il comportamento del run()metodo.
Warren Dew,

1
Non è possibile ignorare il comportamento di a java.lang.Threadignorando il run()metodo. In tal caso, è necessario sostituire il start()metodo immagino. Normalmente riutilizzi il comportamento java.lang.Threaddell'iniezione iniettando il tuo blocco di esecuzione nel run()metodo.
sura2k,

L'eredità non è solo per ignorare alcuni comportamenti, è anche usare comportamenti comuni. Ed è l'opposto, più sostituzioni, la gerarchia peggiore.
peja,

232

Bene, tante buone risposte, voglio aggiungere altro su questo. Questo aiuterà a capire Extending v/s Implementing Thread.
Extends lega i file di due classi molto da vicino e può causare un po 'di difficoltà nel gestire il codice.

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

  1. Quando estendi la classe Thread, dopo non puoi estendere nessuna altra classe richiesta. (Come sai, Java non consente di ereditare più di una classe).
  2. Quando si implementa Runnable, è possibile risparmiare spazio per la classe per estendere qualsiasi altra classe in futuro o ora.

Tuttavia, una differenza significativa tra l'implementazione di Runnable e l'estensione di Thread è quella
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.

L'esempio seguente ti aiuterà 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 the 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 sopra.

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

Nell'approccio dell'interfaccia Runnable, viene creata solo un'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 memoria diversa viene allocata per ogni istanza di classe e ognuna ha un contatore separato, il valore rimane lo stesso, il che significa che non si verificherà alcun incremento poiché nessuno dei riferimenti all'oggetto è uguale.

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

Una classe che implementa Runnable non è un thread e solo una classe. Perché un Runnable diventi un thread, è necessario creare un'istanza di thread e passare se stesso come destinazione.

Nella maggior parte dei casi, l'interfaccia Runnable deve essere utilizzata se si prevede di sovrascrivere il 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 possa aiutare!


40
Il tuo codice è palesemente sbagliato. Voglio dire, fa quello che fa, ma non quello che intendevi mostrare.
zEro,

38
Per chiarire: per il caso eseguibile hai usato la stessa istanza ImplementsRunnable per avviare più thread, mentre per il caso Thread stai creando istanze ExtendsThread diverse che ovviamente portano al comportamento che hai mostrato. La seconda metà del metodo principale dovrebbe essere: ExtendsThread et = new ExtendsThread(); Thread tc1 = new Thread(et); tc1.start(); Thread.sleep(1000); Thread tc2 = new Thread(et); tc2.start(); Thread.sleep(1000); Thread tc3 = new Thread(et); tc3.start(); è più chiara?
zEro,

19
Non capisco ancora il tuo intento, ma il mio punto era che se crei più istanze di ExtendsThread, esse restituiranno tutte 1 (come hai mostrato). È possibile ottenere gli stessi risultati per Runnable facendo la stessa cosa lì, ovvero creando più istanze di ImplementsRunnable.
zEro,

3
@zEro Ciao, vengo dal futuro. Dato che anche la tua versione del codice ha un Threadincremento, l'istruzione è by extending Thread, each of your threads has a unique object associated with it, whereas implementing Runnable, many threads can share the same object instancesbagliata? In caso contrario, qual è il caso che lo dimostra?
Evil Washing Machine

4
Il codice pubblicato qui è fuorviante e può causare mal di testa. Grazie a @zEro per avermi chiarito un po '.
Nikos,

78

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

Se estendi il thread, l'azione che stai facendo sarà sempre in un thread. Tuttavia, se lo si implementa Runnablenon è necessario. Puoi eseguirlo in un thread o passarlo a un qualche tipo di servizio di esecuzione, oppure semplicemente passarlo come attività all'interno di una singola applicazione thread (forse per essere eseguito in un secondo momento, ma all'interno dello stesso thread). Le opzioni sono molto più aperte se usate solo Runnableche se vi impegnate Thread.


6
Bene, puoi effettivamente fare la stessa cosa anche con un Threadoggetto perché Thread implements Runnable... ;-) Ma "ti senti meglio" fare queste cose con un Runnableche farle con un Thread!
siegi,

7
Vero, ma Threadaggiunge molte cose extra che non ti servono e in molti casi non le vuoi. Stai sempre meglio implementando l'interfaccia che corrisponde a ciò che stai effettivamente facendo.
Herms,

74

Se si desidera implementare o estendere qualsiasi altra classe, l' Runnableinterfaccia è preferibile, altrimenti, se non si desidera che qualsiasi altra classe si estenda o si implementa, allora Threadè preferibile la classe.

La differenza più comune è

inserisci qui la descrizione dell'immagine

Durante la extends Threadlezione, successivamente non è possibile estendere qualsiasi altra classe richiesta. (Come sai, Java non consente di ereditare più di una classe).

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

  • Java non supporta più eredità, il che significa che puoi estendere una sola classe in Java, quindi una volta estesa la classe Thread perdi la 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à e modificare o migliorare i comportamenti. Se non stiamo apportando alcuna modifica su Thread, utilizza invece l'interfaccia Runnable.

  • L'interfaccia eseguibile rappresenta un'attività che può essere eseguita da Thread semplice o da Executors o in qualsiasi altro modo. la separazione logica di Task come eseguibile rispetto a Thread è una buona decisione di progettazione.

  • Separare l'attività come eseguibile significa che possiamo riutilizzare l'attività e ha anche la libertà di eseguirla da diversi mezzi. poiché non è possibile riavviare un thread una volta completato. di nuovo Runnable vs Thread per attività, Runnable è il vincitore.

  • Il designer Java lo riconosce ed è per questo che gli Executors accettano Runnable come Task e hanno thread di lavoro che eseguono quel task.

  • Ereditare tutti i metodi Thread è un sovraccarico 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 conosci altre differenze su Thread vs Runnable rispetto a condividi tramite commenti. Personalmente uso 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 extends Threadclassifichi, ogni thread crea un oggetto unico e lo associa. Quando lo fai implements Runnable, condivide lo stesso oggetto su più thread.


73

In realtà, non è saggio confrontare Runnablee Threadtra loro.

Questi due hanno una dipendenza e una relazione nel multi-threading proprio come la Wheel and Enginerelazione del veicolo a motore.

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

Eseguibile:
durante l'implementazione interface Runnablesignifica che stai creando qualcosa che si trova run ablein un thread diverso. Ora creare qualcosa che può essere eseguito all'interno di un thread (eseguibile all'interno di un thread), non significa creare un thread.
Quindi la classe MyRunnablenon è altro che una classe normale con un void runmetodo. E i suoi oggetti saranno alcuni oggetti ordinari con solo un metodo runche verrà eseguito normalmente quando viene chiamato. (a meno che non passiamo l'oggetto in un thread).

Discussione:
class Thread direi Una classe molto speciale con la possibilità di avviare una nuova discussione che abilita il multi-threading attraverso il suo start()metodo.

Perché non è saggio 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 funzionerà e uno lo farà funzionare (come nel caso Wheel and Enginedei veicoli a motore).

Ecco perché non puoi avviare un thread con la MyRunnablenecessità di passarlo a un'istanza di Thread.

Ma è possibile creare ed eseguire un thread solo usando class Threadperché la classe Threadimplementa, Runnablequindi sappiamo tutti che Threadè anche Runnabledentro.

Infine Threade Runnablesi completano a vicenda per il multithreading non concorrente o sostitutivo.


3
Esattamente! Questa dovrebbe essere la risposta accettata. A proposito, penso che la domanda sia stata modificata e ThreadAnon abbia più senso
idelvall

la risposta accettata è molto più delegata grazie per la tua risposta @idelvall
Saif

La migliore risposta! Grazie!
MichaelYe,

44

È necessario implementare Runnable, ma se si esegue su Java 5 o versione successiva, non è necessario avviarlo new Threadma utilizzare un ExecutorService . Per i dettagli, consultare: Come implementare il threading semplice in Java .


5
Non penserei che ExecutorService sarebbe così utile se vuoi solo lanciare un singolo thread.
Powerlord,

1
Da quello che ho appreso non si dovrebbe più iniziare un thread da soli in generale, perché lasciarlo al servizio dell'esecutore rende tutto molto più controllabile (come aspettare che il thread si sospenda). Inoltre, non vedo nulla nella domanda che implica che si tratta di un singolo thread.
Fabian Steeg,

4
Qual è il punto di usare qualsiasi multi-threading se sappiamo che sarà un singolo thread. Quindi supponiamo di avere più thread e questa risposta è preziosa.
zEro,

1
@zEro Sono abbastanza sicuro che ci sia un motivo per cui esiste un solo thread di invio eventi. Dubito che sia l'unico caso in cui è meglio avere un thread separato ma forse non è meglio averne più.
porcoesphino,

33

Non sono un esperto, ma posso pensare a un motivo per implementare Runnable invece di estendere Thread: Java supporta solo una singola eredità, quindi puoi estendere solo una classe.

Modifica: in origine si diceva "L'implementazione di un'interfaccia richiede meno risorse". anche, ma è necessario creare una nuova istanza di Thread in entrambi i modi, quindi era sbagliato.


In runnable non possiamo effettuare chiamate di rete, vero? Come sto avendo android.os.NetworkOnMainThreadException. Ma usando il thread posso effettuare chiamate di rete. Perfavore, correggimi se sbaglio.
Nabeel Thobani,

@NabeelThobani A Java normale non importa, ma sembra Android. Tuttavia, non ho abbastanza familiarità con Android.
Powerlord,

1
@NabeelThobani Certo che puoi. Probabilmente non stai creando un thread con Runnable.
m0skit0,

20

Direi che c'è un terzo modo:

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.


38
Questo non è davvero un terzo modo. Stai ancora implementando Runnable, lo fai solo in modo anonimo.
Don Roby,

2
@Don Roby: che è diverso. È spesso conveniente e puoi usare i campi e le variabili locali finali dalla classe / metodo contenente.
Bart van Heukelom il

6
@BartvanHeukelom È conveniente, ma non diverso. Puoi farlo con qualsiasi tipo di classe nidificata, ovvero classi interne, classi locali ed espressioni lambda.
Xehpuk,

18

Con il rilascio di Java 8, ora esiste una terza opzione.

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

Il tuo esempio può essere sostituito con:

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

o se si desidera utilizzare un ExecutorServicee un riferimento al metodo:

executor.execute(runner::run)

Questi non sono solo molto più brevi dei tuoi esempi, ma presentano anche molti dei vantaggi dichiarati in altre risposte sull'uso di Runnableover Thread, come la singola responsabilità e l'uso della composizione perché non stai specializzando il comportamento del thread. In questo modo evita anche di creare una classe aggiuntiva se tutto ciò che serve è unRunnable come nei propri esempi.


Questa risposta ha bisogno di spiegazioni. Dopo qualche enigma, concludo che () -> {}dovrebbe rappresentare la logica personalizzata di cui qualcuno ha bisogno? Quindi sarebbe meglio dire come () -> { /* Code here */ }?
ToolmakerSteve

17

L'istanza di un'interfaccia offre una separazione più chiara tra il codice e l'implementazione dei thread, quindi preferirei implementare Runnable in questo caso.


12

Tutti qui sembrano pensare che l'implementazione di Runnable sia la strada da percorrere e non sono davvero in disaccordo con loro, ma a mio avviso esiste anche un caso per estendere Thread, in effetti lo avete dimostrato nel vostro codice.

Se si implementa Runnable, la classe che implementa Runnable non ha alcun controllo sul nome del thread, è il codice chiamante che può impostare il nome del thread, in questo modo:

new Thread(myRunnable,"WhateverNameiFeelLike");

ma se estendi Thread, puoi gestirlo all'interno della classe stessa (proprio come nel tuo esempio, dai un nome al thread 'ThreadB'). In questo caso tu:

A) potrebbe dargli un nome più utile ai fini del debug

B) stanno costringendo quel nome a essere usato per tutte le istanze di quella classe (a meno che tu non ignori il fatto che sia un thread e fai quanto sopra con esso come se fosse un Runnable ma stiamo parlando di convenzioni qui in ogni caso così possiamo ignora quella possibilità che sento).

Ad esempio, potresti anche prendere una traccia dello stack della sua creazione e usarla come nome del thread. Questo può sembrare strano ma a seconda di come è strutturato il codice può essere molto utile per scopi di debug.

Potrebbe sembrare una cosa piccola ma dove hai un'applicazione molto complessa con molti thread e all'improvviso le cose "si sono fermate" (o per motivi di deadlock o forse a causa di un difetto in un protocollo di rete che sarebbe inferiore ovvio - o altri motivi infiniti) quindi ottenere un dump dello stack da Java in cui tutti i thread sono chiamati 'Thread-1', 'Thread-2', 'Thread-3' non è sempre molto utile (dipende da come sono i thread strutturato e se puoi utilmente dire quale è solo dalla loro traccia dello stack - non è sempre possibile se stai usando gruppi di più thread che eseguono tutti lo stesso codice).

Detto questo, ovviamente, puoi anche fare quanto sopra in modo generico creando un'estensione della classe thread che imposta il suo nome su una traccia dello stack della sua chiamata di creazione e quindi utilizzalo con le tue implementazioni Runnable anziché la classe Thread java standard (vedi sotto) ma oltre alla traccia dello stack potrebbero esserci più informazioni specifiche sul contesto che potrebbero essere utili nel nome del thread per il debug (un riferimento a una delle molte code o socket che potrebbe elaborare ad esempio nel qual caso potresti preferire estendi il Thread appositamente per quel caso in modo che tu possa costringere il compilatore a costringere te (o altri che usano le tue librerie) a passare determinate informazioni (ad esempio la coda / socket in questione) per l'uso nel nome).

Ecco un esempio del thread generico con la traccia dello stack chiamante come nome:

public class DebuggableThread extends Thread {
    private static String getStackTrace(String name) {
        Throwable t= new Throwable("DebuggableThread-"+name);
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        PrintStream ps = new PrintStream(os);
        t.printStackTrace(ps);
        return os.toString();
    }

    public DebuggableThread(String name) {
        super(getStackTrace(name));
    }

    public static void main(String[] args) throws Exception {
        System.out.println(new Thread());
        System.out.println(new DebuggableThread("MainTest"));
    }
}

ed ecco un esempio dell'output che confronta i due nomi:

Thread[Thread-1,5,main]
Thread[java.lang.Throwable: DebuggableThread-MainTest
    at DebuggableThread.getStackTrace(DebuggableThread.java:6)
    at DebuggableThread.<init>(DebuggableThread.java:14)
    at DebuggableThread.main(DebuggableThread.java:19)
,5,main]

cHao qual è il tuo punto? Non è possibile utilizzare il codice sopra riportato durante l'esecuzione del thread per ottenere uno stacktrace della creazione dei thread (invece si otterrebbe un nome semplice o nella migliore delle ipotesi uno stacktrace dell'avvio dei thread) ma effettuando la sottoclasse del thread è possibile fare esattamente questo e forzare esso, anche richiedendo ulteriori informazioni specifiche sul contesto, dandoti così una comprensione più concreta di quale thread potrebbe avere un problema.
AntonyM,

8
Il mio punto è che "Se si implementa Runnable, la classe che implementa Runnable non ha alcun controllo sul nome del thread ..." è palesemente falso. Un'implementazione di classe Runnablepuò effettivamente controllare il nome del thread, poiché il thread che esegue il codice è per definizione il thread corrente (e qualsiasi codice che supera i controlli di sicurezza ha il controllo sui nomi dei thread). Considerando che dedichi metà del tuo post a "omg, che dire dei nomi delle discussioni!", Sembra un grosso problema.
cHao,

Il nome della discussione? Nulla ti impedisce di estendere anche la classe thread.
RichieHH,

11

Eseguibile perché:

  • Lascia una maggiore flessibilità all'implementazione Runnable per estendere un'altra classe
  • Separa il codice dall'esecuzione
  • Consente di eseguire il runnable da un pool di thread, dal thread degli eventi o in qualsiasi altro modo in futuro.

Anche se non ti serve nulla di tutto questo, potresti farlo in futuro. Poiché non vi è alcun vantaggio nel sovrascrivere Thread, Runnable è una soluzione migliore.


11

Poiché questo è un argomento molto popolare e le buone risposte sono diffuse ovunque e trattate in modo molto approfondito, ho sentito che è giustificabile compilare le buone risposte degli altri in una forma più concisa, quindi i nuovi arrivati ​​hanno una facile panoramica in anticipo:

  1. In genere si estende una classe per aggiungere o modificare funzionalità. Quindi, se non si vuole a sovrascrivere qualsiasi comportamento Discussione , quindi utilizzare Runnable.

  2. Allo stesso modo, se non hai bisogno di ereditare i metodi di thread, puoi fare a meno di quel sovraccarico usando Runnable.

  3. Eredità singola : se estendi Thread non puoi estenderti da nessun'altra classe, quindi se è quello che devi fare, devi usare Runnable.

  4. È buona norma separare la logica del dominio dai mezzi tecnici, in tal senso è meglio avere un'attività eseguibile che isola l' attività dal proprio corridore .

  5. Puoi eseguire lo stesso oggetto Runnable più volte , ma un oggetto Thread può essere avviato solo una volta. (Forse il motivo, il motivo per cui gli esecutori accettano Runnables, ma non i thread.)

  6. Se sviluppi il tuo compito come Runnable, hai tutta la flessibilità su come usarlo ora e in futuro . Puoi farlo funzionare contemporaneamente tramite Executors ma anche tramite Thread. E puoi anche usarlo / chiamarlo non contemporaneamente nello stesso thread proprio come qualsiasi altro tipo / oggetto ordinario.

  7. In questo modo è anche più facile da separati task-logica e di concorrenza aspetti nei vostri test di unità .

  8. Se sei interessato a questa domanda, potresti anche essere interessato alla differenza tra Callable e Runnable .


@Pino Sì, anche il thread stesso è eseguibile. Tuttavia, se lo estendi per usarlo solo come Runnable, qual è il punto? Perché non usare un semplice Runnable senza tutto il bagaglio. Quindi, direi che se estendi Thread, lo eseguiresti anche usando il suo metodo start, che può essere usato solo una volta. Questo è il punto che Nidhish-Krishnan voleva esprimere nella sua risposta. Nota che il mio è solo una raccolta o un breve riassunto di altre risposte qui.
Jörg

11

Le differenze tra Extending Thread e Implementing Runnable sono:

inserisci qui la descrizione dell'immagine


8

Questo è discusso nell'esercitazione sulla definizione e l'avvio di un thread di Oracle :

Quale di questi modi di dire dovresti usare? Il primo linguaggio, che impiega un oggetto Runnable, è più generale, poiché l'oggetto Runnable può sottoclassare una classe diversa da Thread. Il secondo linguaggio è più facile da usare in applicazioni semplici, ma è limitato dal fatto che la classe di attività deve essere un discendente di Thread. Questa lezione si concentra sul primo approccio, che separa l'attività Runnable dall'oggetto Thread che esegue l'attività. Questo approccio non solo è più flessibile, ma è applicabile alle API di gestione dei thread di alto livello descritte più avanti.

In altre parole, l'implementazione Runnablefunzionerà in scenari in cui la classe estende una classe diversa da Thread. Java non supporta l'ereditarietà multipla. Inoltre, l'estensione Threadnon sarà possibile quando si utilizzano alcune API di gestione thread di alto livello. L'unico scenario in cui Threadè preferibile l' estensione è in una piccola applicazione che non sarà soggetta ad aggiornamenti in futuro. È quasi sempre meglio implementarlo Runnablein quanto è più flessibile man mano che il progetto cresce. Una modifica del design non avrà un impatto importante in quanto è possibile implementare molte interfacce in Java, ma solo una classe.


8

La spiegazione più semplice sarebbe implementando Runnablepossiamo assegnare lo stesso oggetto a più thread e ognuno Threadcondivide gli stessi stati e comportamenti dell'oggetto.

Ad esempio, supponiamo che ci siano due thread, thread1 inserisce un numero intero in un array e thread2 prende numeri interi dall'array quando l'array viene riempito. Si noti che per il funzionamento di thread2 è necessario conoscere lo stato dell'array, indipendentemente dal fatto che thread1 lo abbia riempito o meno.

L'implementazione Runnableti consente di avere questa flessibilità per condividere l'oggetto, mentre extends Threadti consente di creare nuovi oggetti per ogni thread, quindi qualsiasi aggiornamento che viene fatto da thread1 viene perso in thread2.


7

Se non sbaglio, è più o meno simile a

Qual è la differenza tra un'interfaccia e una classe astratta?

extends stabilisce la relazione " Is A " e l'interfaccia fornisce la funzionalità " Ha una ".

Preferisci attrezzi Runnable :

  1. Se non è necessario estendere la classe Thread e modificare l'implementazione predefinita dell'API Thread
  2. Se stai eseguendo un incendio e dimentica il comando
  3. Se stai già estendendo un'altra classe

Preferisci " estende la discussione ":

  1. Se devi sovrascrivere uno di questi metodi di discussione come elencato nella pagina della documentazione di Oracle

Generalmente non è necessario sovrascrivere il comportamento del thread. Così strumenti Runnable sono preferiti per la maggior parte delle volte.

Su una nota diversa, usando avanzato ExecutorServiceoThreadPoolExecutorService API offre maggiore flessibilità e controllo.

Dai un'occhiata a questa domanda SE:

ExecutorService vs Casual Thread Spawner


5

La separazione della classe Thread dall'implementazione Runnable evita anche potenziali problemi di sincronizzazione tra il thread e il metodo run (). Un Runnable separato generalmente offre una maggiore flessibilità nel modo in cui il codice eseguibile viene referenziato ed eseguito.


5

Questa è la S di SOLID : singola responsabilità.

Un thread incarna il contesto corrente (come nel contesto di esecuzione: stack frame, ID thread, ecc.) Dell'esecuzione asincrona di un pezzo di codice. Idealmente, quel pezzo di codice dovrebbe essere la stessa implementazione, sia sincrona che asincrona .

Se li raggruppi in un'unica implementazione, dai all'oggetto risultante due cause di modifica non correlate :

  1. gestione dei thread nella tua applicazione (es. query e modifica del contesto di esecuzione)
  2. algoritmo implementato dal pezzo di codice (la parte eseguibile)

Se il linguaggio che usi supporta classi parziali o ereditarietà multiple, puoi separare ciascuna causa nella sua superclasse, ma si riduce allo stesso modo della composizione dei due oggetti, poiché i loro set di funzionalità non si sovrappongono. Questo è per la teoria.

In pratica, in generale, un programma non deve comportare più complessità del necessario. Se hai un thread che lavora su un'attività specifica, senza mai cambiarla, probabilmente non ha senso separare le attività dalle classi e il tuo codice rimane più semplice.

Nel contesto di Java , poiché la struttura è già lì , è probabilmente più facile iniziare direttamente con Runnableclassi autonome e passare le loro istanze a Thread(o Executor) istanze. Una volta utilizzato per quel modello, non è più difficile da usare (o addirittura leggere) rispetto al semplice caso di thread eseguibile.


5

Un motivo per cui vorresti implementare un'interfaccia piuttosto che estendere una classe base è che stai già estendendo un'altra classe. È possibile estendere solo una classe, ma è possibile implementare qualsiasi numero di interfacce.

Se estendi Thread, stai praticamente impedendo che la tua logica venga eseguita da qualsiasi altro thread diverso da 'this'. Se vuoi solo qualche thread per eseguire la tua logica, è meglio implementare Runnable.


Sì, implementando l'interfaccia Runnable per essere liberi di implementare la propria logica estendendo qualsiasi classe, ecco perché Runnable è principalmente preferito alla classe Thread.
Akash5288,

5

se usi runnable puoi salvare lo spazio per estenderlo a qualsiasi altra tua classe.


5

Possiamo rivedere il motivo di base per cui volevamo che la nostra classe si comportasse come una Thread ? Non vi è alcun motivo, volevamo solo eseguire un'attività, molto probabilmente in modalità asincrona, il che significa precisamente che l'esecuzione dell'attività deve derivare dal nostro thread principale e dal thread principale se finisce in anticipo, può o meno attendere per il percorso ramificato (attività).

Se questo è lo scopo, allora dove vedo la necessità di un thread specializzato. Ciò può essere ottenuto raccogliendo un thread RAW dal pool di thread del sistema e assegnandogli il nostro compito (può essere un'istanza della nostra classe) e basta.

Quindi obbediamo al concetto di OOP e scriviamo una classe del tipo di cui abbiamo bisogno. Ci sono molti modi per fare le cose, farlo nel modo giusto conta.

Abbiamo bisogno di un'attività, quindi scrivi una definizione di attività che può essere eseguita su un thread. Quindi usa Runnable.

Ricorda sempre che implementsè appositamente usato per impartire un comportamento eextends viene utilizzato per impartire una caratteristica / proprietà.

Non vogliamo la proprietà del thread, ma vogliamo che la nostra classe si comporti come un'attività che può essere eseguita.


4

Sì, se si chiama la chiamata ThreadA, non è necessario chiamare il metodo start ed eseguire il metodo è call dopo call solo la classe ThreadA. Ma se si utilizza la chiamata ThreadB, è necessario il thread di avvio necessario per il metodo di esecuzione della chiamata. Se hai altro aiuto, rispondimi.


4

Trovo che sia molto utile usare Runnable per tutti i motivi menzionati, ma a volte mi piace estendere Thread in modo da poter creare il mio metodo di arresto del thread e chiamarlo direttamente sul thread che ho creato.


4

Java non supporta l'ereditarietà multipla, quindi se estendi la classe Thread non verrà estesa nessuna altra classe.

Ad esempio: se si crea un'applet, questa deve estendere la classe dell'applet, quindi qui l'unico modo per creare thread è implementando l'interfaccia Runnable


4

Runnableè un'interfaccia, mentre Threadè una classe che implementa questa interfaccia. Dal punto di vista della progettazione, dovrebbe esserci una netta separazione tra il modo in cui un'attività viene definita e quella in cui viene eseguita. Il primo è la responsabilità di Runnalbeun'implementazione e il secondo è un lavoro di Threadclasse. Nella maggior parte dei casi, l'implementazione Runnableè il modo giusto di seguire.


4

Differenza tra Thread e eseguibile. Se stiamo creando Thread utilizzando la classe Thread, allora Numero di thread uguale al numero di oggetto che abbiamo creato. Se stiamo creando thread implementando l'interfaccia eseguibile, allora possiamo usare un singolo oggetto per creare più thread, quindi un singolo oggetto è condiviso da più thread, quindi ci vorrà meno memoria

Quindi a seconda del requisito se i nostri dati non sono sensibili. Quindi può essere condiviso tra più thread che possiamo usare l'interfaccia Runnable.


4

Aggiungendo qui i miei due centesimi - Usalo sempre quando possibile implements Runnable. Di seguito sono riportati due avvertimenti sul perché non dovresti usare extends Threads

  1. Idealmente non dovresti mai estendere la classe Thread; la Threadclasse dovrebbe essere fatta final. Almeno i suoi metodi piace thread.getId(). Vedi questa discussione per un bug relativo all'estensione di Threads.

  2. Coloro che amano risolvere enigmi possono vedere un altro effetto collaterale dell'estensione del thread. Il codice seguente stamperà il codice non raggiungibile quando nessuno li sta notificando.

Si prega di consultare http://pastebin.com/BjKNNs2G .

public class WaitPuzzle {

    public static void main(String[] args) throws InterruptedException {
        DoNothing doNothing = new DoNothing();
        new WaitForever(doNothing).start();
        new WaitForever(doNothing).start();
        new WaitForever(doNothing).start();
        Thread.sleep(100);
        doNothing.start();
        while(true) {
            Thread.sleep(10);
        }
    }


    static class WaitForever extends  Thread {

        private DoNothing doNothing;

        public WaitForever(DoNothing doNothing) {
            this.doNothing =  doNothing;
        }

        @Override
        public void run() {
            synchronized (doNothing) {
                try {
                    doNothing.wait(); // will wait forever here as nobody notifies here
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Unreachable Code");
            }
        }
    }

    static class DoNothing extends Thread {

        @Override
        public void run() {
            System.out.println("Do Nothing ");
        }
    } 
}
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.