«Implémente Runnable» vs «étend Thread» en Java

2119

Depuis le temps que j'ai passé avec les threads en Java, j'ai trouvé ces deux façons d'écrire des threads:

Avec implements Runnable:

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

Ou, avec extends Thread:

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

Y a-t-il une différence significative entre ces deux blocs de code?

user65374
la source
57
Merci pour cette question, les réponses ont éclairci beaucoup d'idées fausses que j'avais. J'ai cherché la bonne façon de faire des threads Java avant l'existence de SO et il y avait beaucoup d'informations erronées / obsolètes.
James McMahon
5
il y a une raison pour laquelle vous voudrez peut-être étendre Thread ( mais je ne le recommande pas ), vous pouvez le gérer de manière préventive interrupt(). Encore une fois, c'est une idée, elle pourrait être utile dans le bon cas, mais je ne la recommande pas.
bestsss
Veuillez également voir la réponse, bien expliquée: stackoverflow.com/q/5562720/285594
@bestsss, j'essaie de comprendre ce que vous pourriez vouloir dire à propos de la gestion d'interruption (). Essayez-vous de remplacer la méthode?
Bob Cross
8
Oui, selon le code, la classe Thread A peut étendre n'importe quelle classe tandis que la classe Thread B ne peut pas étendre n'importe quelle autre classe
mani deepak

Réponses:

1673

Oui: l'implémentation Runnableest la façon préférée de le faire, OMI. Vous ne spécialisez pas vraiment le comportement du thread. Vous lui donnez juste quelque chose à exécuter. Cela signifie que la composition est la voie à suivre philosophiquement "la plus pure".

En termes pratiques , cela signifie que vous pouvez également implémenter Runnableet étendre à partir d'une autre classe.

Jon Skeet
la source
151
Exactement, bien dit. Quel comportement essayons-nous d'écraser dans Thread en l'étendant? Je dirais que la plupart des gens n'essaient pas d'écraser un comportement, mais essaient d'utiliser le comportement de Thread.
hooknc
87
En guise de commentaire secondaire, si vous instanciez un thread et n'appelez pas sa méthode start (), vous créez une fuite de mémoire dans Java <5 (cela ne se produit pas avec Runnables): stackoverflow.com/questions/107823/…
Nacho Coloma
25
Un avantage mineur de Runnable est que si, dans certaines circonstances, vous ne vous souciez pas ou si vous ne souhaitez pas utiliser le threading et que vous souhaitez simplement exécuter le code, vous avez la possibilité d'appeler simplement run (). par exemple (très ondulé) if (numberCores > 4) myExecutor.excute(myRunnable); else myRunnable.run()
user949300
10
@ user949300, vous pouvez également le faire avec extends Threadet si vous ne voulez pas de thread, pourquoi implémenteriez-vous même Runnable...
m0skit0
30
Pour paraphraser Sierra et Bates, un avantage clé de l'implémentation de Runnable est que vous séparez architecturalement le "job" du "runner".
8bitjunkie
569

tl; dr: implémente Runnable, c'est mieux. Cependant, la mise en garde est importante

En général, je recommanderais d'utiliser quelque chose comme Runnableplutôt que Threadparce qu'il vous permet de garder votre travail associé de manière lâche à votre choix de concurrence. Par exemple, si vous utilisez un Runnableet décidez plus tard que cela ne nécessite pas le sien Thread, vous pouvez simplement appeler threadA.run ().

Mise en garde: ici, je déconseille fortement l'utilisation de threads bruts. Je préfère de beaucoup l'utilisation de appelables et FutureTasks (De la javadoc: « Un calcul asynchrone résiliable »). L'intégration des délais d'attente, l'annulation correcte et la mise en commun des threads de la prise en charge de la concurrence moderne me sont beaucoup plus utiles que des piles de threads bruts.

Suivi: il existe un FutureTaskconstructeur qui vous permet d'utiliser Runnables (si c'est ce avec quoi vous êtes le plus à l'aise) tout en bénéficiant des outils de concurrence modernes. Pour citer le javadoc :

Si vous n'avez pas besoin d'un résultat particulier, envisagez d'utiliser des constructions du formulaire:

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

Donc, si nous les remplaçons runnablepar les vôtres threadA, nous obtenons ce qui suit:

new FutureTask<Object>(threadA, null)

Une autre option qui vous permet de rester plus proche de Runnables est un ThreadPoolExecutor . Vous pouvez utiliser la méthode execute pour passer un Runnable pour exécuter "la tâche donnée dans le futur".

Si vous souhaitez essayer d'utiliser un pool de threads, le fragment de code ci-dessus deviendrait quelque chose comme ceci (en utilisant la méthode d'usine Executors.newCachedThreadPool () ):

ExecutorService es = Executors.newCachedThreadPool();
es.execute(new ThreadA());
Bob Cross
la source
40
C'est mieux que la réponse acceptée à mon humble avis. Une chose: l'extrait de code que vous avez ne ferme pas l'exécuteur et je vois des millions de questions où les gens se trompent, créant un nouvel exécuteur chaque fois qu'ils veulent lancer une tâche. esserait mieux comme un champ statique (ou injecté) donc il n'est créé qu'une seule fois.
artbristol
8
@artbristol, merci! Je ne suis pas en désaccord sur le nouvel exécuteur (nous faisons ce que vous proposez dans notre code). En écrivant la réponse originale, j'essayais d'écrire un code minimal analogue au fragment original. Nous devons espérer que de nombreux lecteurs de ces réponses les utilisent comme points de départ. Je n'essaie pas d'écrire un remplacement pour le javadoc. J'écris effectivement du matériel marketing pour cela: si vous aimez cette méthode, vous devriez voir toutes les autres grandes choses que nous avons à offrir ...!
Bob Cross
3
Je sais que je suis un peu en retard à ce sujet, mais traiter FutureTaskdirectement n'est généralement pas ce que vous voulez faire. ExecutorServices créera ce qui Futurevous convient lorsque vous submita Runnable/ Callableà eux. De même pour ScheduledExecutorServices et ScheduledFuturequand vous scheduleun Runnable/ Callable.
Powerlord
3
@Powerlord, mon intention était de créer des fragments de code qui correspondent le plus possible aux OP. Je suis d'accord que la nouvelle FutureTask n'est pas optimale mais elle est claire aux fins d'explication.
Bob Cross
258

Morale de l'histoire:

Héritez uniquement si vous souhaitez remplacer un comportement.

Ou plutôt il doit être lu comme:

Héritez moins, interférez plus.

panzerschreck
la source
1
Cela devrait toujours être la question si vous commencez à créer un objet fonctionnant simultanément! Avez-vous même besoin des fonctions Thread Object?
Liebertee
4
En héritant de Thread, on veut presque toujours remplacer le comportement de la run()méthode.
Warren Dew
1
Vous ne pouvez pas remplacer le comportement d'un java.lang.Threaden remplaçant la run()méthode. Dans ce cas, vous devez remplacer la start()méthode, je suppose. Normalement, vous réutilisez simplement le comportement du java.lang.Threaden injectant votre bloc d'exécution dans la run()méthode.
sura2k
L'héritage n'est pas seulement destiné à remplacer certains comportements, il consiste également à utiliser des comportements courants. Et c'est le contraire, plus il y a de priorité, plus la hiérarchie empire.
peja
232

Eh bien tant de bonnes réponses, je veux ajouter plus à ce sujet. Cela vous aidera à comprendre Extending v/s Implementing Thread.
Extends lie très étroitement deux fichiers de classe et peut rendre le code assez difficile à gérer.

Les deux approches font le même travail mais il y a eu quelques différences.
La différence la plus courante est

  1. Lorsque vous étendez la classe Thread, après cela, vous ne pouvez pas étendre toute autre classe dont vous avez besoin. (Comme vous le savez, Java ne permet pas d'hériter de plus d'une classe).
  2. Lorsque vous implémentez Runnable, vous pouvez économiser de l'espace pour votre classe afin d'étendre toute autre classe à l'avenir ou maintenant.

Cependant, une différence significative entre l'implémentation de Runnable et l'extension de Thread est que
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'exemple suivant vous aidera à mieux comprendre

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

Sortie du programme ci-dessus.

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

Dans l'approche de l'interface exécutable, une seule instance d'une classe est en cours de création et elle a été partagée par différents threads. Ainsi, la valeur du compteur est incrémentée pour chaque accès au thread.

Alors que, l'approche de la classe Thread, vous devez créer une instance distincte pour chaque accès au thread. Par conséquent, une mémoire différente est allouée pour chaque instance de classe et chacune a un compteur séparé, la valeur reste la même, ce qui signifie qu'aucun incrément ne se produira car aucune des références d'objet n'est identique.

Quand utiliser Runnable?
Utilisez l'interface exécutable lorsque vous souhaitez accéder aux mêmes ressources à partir du groupe de threads. Évitez d'utiliser la classe Thread ici, car la création d'objets multiples consomme plus de mémoire et cela devient une surcharge importante de performances.

Une classe qui implémente Runnable n'est pas un thread et juste une classe. Pour qu'un Runnable devienne un Thread, vous devez créer une instance de Thread et vous passer comme cible.

Dans la plupart des cas, l'interface Runnable doit être utilisée si vous prévoyez uniquement de remplacer la run()méthode et aucune autre méthode Thread. Ceci est important car les classes ne doivent pas être sous-classées sauf si le programmeur a l'intention de modifier ou d'améliorer le comportement fondamental de la classe.

Lorsqu'il est nécessaire d'étendre une superclasse, l'implémentation de l'interface Runnable est plus appropriée que l'utilisation de la classe Thread. Parce que nous pouvons étendre une autre classe tout en implémentant l'interface Runnable pour créer un thread.

J'espère que cela vous aidera!

Rupesh Yadav
la source
40
Votre code est manifestement faux. Je veux dire, il fait ce qu'il fait, mais pas ce que vous aviez l'intention de montrer.
zEro
38
Pour clarifier: pour le cas exécutable, vous avez utilisé la même instance ImplementsRunnable pour démarrer plusieurs threads, tandis que pour le cas Thread, vous créez différentes instances ExtendsThread, ce qui conduit évidemment au comportement que vous avez montré. La deuxième moitié de votre méthode principale devrait être: 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(); est-ce plus clair?
zEro
19
Je ne comprends pas encore votre intention, mais mon point était que si vous créez plusieurs instances de ExtendsThread - elles renverront toutes 1 (comme vous l'avez montré). Vous pouvez obtenir les mêmes résultats pour Runnable en faisant la même chose, c'est-à-dire en créant plusieurs instances de ImplementsRunnable.
zEro
3
@zEro Salut, je viens du futur. Étant donné que votre version du code est également Threadincrémentée, la déclaration est-elle by extending Thread, each of your threads has a unique object associated with it, whereas implementing Runnable, many threads can share the same object instancealors fausse? Sinon, quel est le cas qui le démontre?
Evil Washing Machine
4
Le code affiché ici est trompeur et peut entraîner des maux de tête. Merci à @zEro de l'avoir éclairci un peu.
Nikos
78

Une chose qui m'étonne n'a pas encore été mentionnée: l'implémentation Runnablerend votre classe plus flexible.

Si vous étendez le thread, l'action que vous faites sera toujours dans un thread. Cependant, si vous l'implémentez, Runnablecela ne doit pas l'être. Vous pouvez l'exécuter dans un thread, ou le transmettre à une sorte de service d'exécuteur, ou simplement le passer comme tâche dans une seule application threadée (peut-être à exécuter ultérieurement, mais dans le même thread). Les options sont beaucoup plus ouvertes si vous utilisez simplement Runnableque si vous vous y liez Thread.

Herms
la source
6
Eh bien, vous pouvez en fait faire la même chose avec un Threadobjet aussi parce que Thread implements Runnable… ;-) Mais ça «se sent mieux» de faire ces choses avec un Runnableque de les faire avec un Thread!
siegi
8
C'est vrai, mais Threadajoute beaucoup de choses supplémentaires dont vous n'avez pas besoin, et dans de nombreux cas, vous ne voulez pas. Il vaut toujours mieux mettre en œuvre l'interface qui correspond à ce que vous faites réellement.
Herms
74

Si vous voulez implémenter ou étendre une autre classe, alors l' Runnableinterface est plus préférable, sinon, si vous ne voulez pas qu'une autre classe s'étende ou implémente, alors la Threadclasse est préférable.

La différence la plus courante est

entrez la description de l'image ici

Quand vous extends Thread classe, après cela, vous ne pouvez pas étendre toute autre classe dont vous aviez besoin. (Comme vous le savez, Java ne permet pas d'hériter de plus d'une classe).

Lorsque vous implements Runnable, vous pouvez économiser de l'espace pour votre classe afin d'étendre n'importe quelle autre classe à l'avenir ou maintenant.

  • Java ne prend pas en charge plusieurs héritages, ce qui signifie que vous ne pouvez étendre qu'une seule classe en Java, donc une fois que vous avez étendu la classe Thread, vous avez perdu votre chance et ne pouvez pas étendre ou hériter d'une autre classe en Java.

  • Dans la programmation orientée objet, l'extension d'une classe signifie généralement l'ajout de nouvelles fonctionnalités et la modification ou l'amélioration des comportements. Si nous n'effectuons aucune modification sur Thread, utilisez plutôt l'interface Runnable.

  • L'interface exécutable représente une tâche qui peut être exécutée par un thread simple ou des exécuteurs ou tout autre moyen. la séparation logique de Task comme Runnable que Thread est une bonne décision de conception.

  • Séparer la tâche en tant que Runnable signifie que nous pouvons réutiliser la tâche et avoir également la liberté de l'exécuter à partir de différents moyens. car vous ne pouvez pas redémarrer un thread une fois qu'il est terminé. Runnable vs Thread pour la tâche, Runnable est gagnant.

  • Le concepteur Java le reconnaît et c'est pourquoi les exécuteurs acceptent Runnable comme tâche et ils ont un thread de travail qui exécute ces tâches.

  • L'héritage de toutes les méthodes Thread est une surcharge supplémentaire juste pour représenter une tâche qui peut être effectuée facilement avec Runnable.

Gracieuseté de javarevisited.blogspot.com

Ce sont quelques-unes des différences notables entre Thread et Runnable en Java. Si vous connaissez d'autres différences sur Thread vs Runnable que s'il vous plaît partagez-les via des commentaires. J'utilise personnellement Runnable sur Thread pour ce scénario et recommande d'utiliser l'interface Runnable ou Callable en fonction de vos besoins.

Cependant, la différence significative est.

Lorsque vous extends Threadclasse, chacun de vos threads crée un objet unique et s'associe avec lui. Lorsque vous implements Runnable, il partage le même objet sur plusieurs threads.

Nidhish Krishnan
la source
73

En fait, il est sage de ne pas comparer Runnableet Threadles uns avec les autres.

Ces deux ont une dépendance et une relation dans le multi-threading tout comme la Wheel and Enginerelation du véhicule à moteur.

Je dirais qu'il n'y a qu'une seule façon pour le multi-thread avec deux étapes. Permettez-moi de faire valoir mon point de vue.

Exécutable:
lors de l'implémentation, interface Runnablecela signifie que vous créez quelque chose qui se trouve run abledans un thread différent. Maintenant, créer quelque chose qui peut s'exécuter à l'intérieur d'un thread (exécutable à l'intérieur d'un thread), ne signifie pas créer un thread.
La classe MyRunnablen'est donc rien d'autre qu'une classe ordinaire avec une void runméthode. Et ses objets seront des objets ordinaires avec seulement une méthode runqui s'exécutera normalement lors de l'appel. (sauf si nous passons l'objet dans un fil).

Thread:,
class Thread je dirais Une classe très spéciale avec la capacité de démarrer un nouveau Thread qui permet en fait le multi-threading via sa start()méthode.

Pourquoi pas sage de comparer?
Parce que nous avons besoin des deux pour le multi-threading.

Pour le multi-threading, nous avons besoin de deux choses:

  • Quelque chose qui peut fonctionner à l'intérieur d'un Thread (Runnable).
  • Quelque chose qui peut démarrer un nouveau thread (Thread).

Donc, techniquement et théoriquement, les deux sont nécessaires pour démarrer un thread, l'un fonctionnera et l'autre le fera fonctionner (comme Wheel and Enginepour un véhicule à moteur).

C'est pourquoi vous ne pouvez pas démarrer un thread avec lequel vous MyRunnabledevez le passer à une instance de Thread.

Mais il est possible de créer et d'exécuter un thread uniquement en utilisant class Threadparce que la classe Threadimplémente Runnabledonc nous savons tous que c'est Threadaussi un Runnableintérieur.

Enfin Threadet Runnablesont complémentaires les uns des autres pour le multithreading non concurrent ou de remplacement.

Saif
la source
3
Exactement! Ce devrait être la réponse acceptée. BTW Je pense que la question a été modifiée et ThreadAn'a plus de sens
idelvall
la réponse acceptée est beaucoup plus déléguée merci pour votre réponse @idelvall
Saif
La meilleure réponse! Merci!
MichaelYe
44

Vous devez implémenter Runnable, mais si vous utilisez Java 5 ou supérieur, vous ne devez pas le démarrer avec new Threadmais utiliser un ExecutorService à la place. Pour plus de détails, voir: Comment implémenter un filetage simple en Java .

Fabian Steeg
la source
5
Je ne pense pas que ExecutorService serait si utile si vous souhaitez simplement lancer un seul thread.
Powerlord
1
D'après ce que j'ai appris, il ne faut plus démarrer un thread par vous-même en général, car laisser cela au service de l'exécuteur rend tout beaucoup plus contrôlable (comme, attendre que le thread se suspende). De plus, je ne vois rien dans la question qui implique qu'il s'agit d'un seul thread.
Fabian Steeg,
4
Quel est l'intérêt d'utiliser un multi-threading si nous savons avant qu'il ne s'agira que d'un thread unique. Supposons donc que nous avons plusieurs threads et cette réponse est précieuse.
zEro
1
@zEro Je suis presque sûr qu'il y a une raison pour laquelle il n'y a qu'un seul thread de répartition d'événements. Je doute que ce soit le seul cas où il est préférable d'avoir un thread séparé mais peut-être pas préférable d'en avoir plusieurs.
porcoesphino
33

Je ne suis pas un expert, mais je peux penser à une raison pour implémenter Runnable au lieu d'étendre Thread: Java ne prend en charge que l'héritage unique, vous ne pouvez donc étendre qu'une seule classe.

Edit: Cela disait à l'origine "L'implémentation d'une interface nécessite moins de ressources." également, mais vous devez créer une nouvelle instance de thread de toute façon, donc c'était faux.

Powerlord
la source
Dans runnable, nous ne pouvons pas faire d'appels réseau, n'est-ce pas? Comme je rencontre android.os.NetworkOnMainThreadException. Mais en utilisant du fil, je peux passer des appels réseau. S'il vous plait corrigez moi si je me trompe.
Nabeel Thobani
@NabeelThobani Normal Java s'en fiche, mais cela ressemble à Android. Cependant, je ne connais pas assez bien Android.
Powerlord
1
@NabeelThobani Bien sûr que vous le pouvez. Vous ne créez probablement pas de thread avec votre Runnable.
m0skit0
20

Je dirais qu'il y a une troisième voie:

public class Something {

    public void justAnotherMethod() { ... }

}

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

Peut-être que cela est un peu influencé par ma récente utilisation intensive de Javascript et Actionscript 3, mais de cette façon, votre classe n'a pas besoin d'implémenter une interface assez vague comme Runnable.

Bart van Heukelom
la source
38
Ce n'est pas vraiment une troisième voie. Vous implémentez toujours Runnable, vous le faites simplement de manière anonyme.
Don Roby
2
@Don Roby: Ce qui est différent. C'est souvent pratique, et vous pouvez utiliser des champs et des variables locales finales de la classe / méthode contenante.
Bart van Heukelom
6
@BartvanHeukelom C'est pratique, mais pas différent. Vous pouvez le faire avec n'importe quel type de classe imbriquée, c'est-à-dire les classes internes, les classes locales et les expressions lambda.
xehpuk
18

Avec la sortie de Java 8, il y a maintenant une troisième option.

Runnableest une interface fonctionnelle , ce qui signifie que ses instances peuvent être créées avec des expressions lambda ou des références de méthode.

Votre exemple peut être remplacé par:

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

ou si vous souhaitez utiliser une ExecutorServiceréférence de méthode et:

executor.execute(runner::run)

Ce sont non seulement beaucoup plus courte que vos exemples, mais viennent aussi avec un grand nombre des avantages énoncés dans d' autres réponses de l' utilisation de Runnableplus Thread, comme la responsabilité unique et la composition à l' aide parce que vous n'êtes pas spécialisé le comportement du fil. De cette façon, vous évitez également de créer une classe supplémentaire si tout ce dont vous avez besoin est un Runnablecomme vous le faites dans vos exemples.

Alex
la source
Cette réponse doit être expliquée. Après quelques énigmes, je conclus que () -> {}c'est censé représenter la logique personnalisée dont quelqu'un a besoin? Il serait donc préférable de le dire comme () -> { /* Code here */ }?
ToolmakerSteve
17

L'instanciation d'une interface donne une séparation plus nette entre votre code et l'implémentation des threads, donc je préfère implémenter Runnable dans ce cas.

starblue
la source
12

Tout le monde ici semble penser que l'implémentation de Runnable est la voie à suivre et je ne suis pas vraiment en désaccord avec eux, mais il y a aussi un argument pour étendre Thread à mon avis, en fait, vous l'avez en quelque sorte démontré dans votre code.

Si vous implémentez Runnable, la classe qui implémente Runnable n'a aucun contrôle sur le nom du thread, c'est le code appelant qui peut définir le nom du thread, comme ceci:

new Thread(myRunnable,"WhateverNameiFeelLike");

mais si vous étendez Thread, vous pouvez gérer cela au sein de la classe elle-même (tout comme dans votre exemple, vous nommez le thread 'ThreadB'). Dans ce cas, vous:

A) pourrait lui donner un nom plus utile à des fins de débogage

B) forcent à ce que ce nom soit utilisé pour toutes les instances de cette classe (à moins que vous ignoriez le fait qu'il s'agit d'un thread et que vous fassiez ce qui précède comme s'il s'agissait d'un Runnable, mais nous parlons ici de convention dans tous les cas, donc ignorer cette possibilité que je ressens).

Vous pouvez même par exemple prendre une trace de pile de sa création et l'utiliser comme nom de thread. Cela peut sembler étrange, mais selon la structure de votre code, il peut être très utile à des fins de débogage.

Cela peut sembler être une petite chose mais où vous avez une application très complexe avec beaucoup de threads et tout d'un coup, les choses se sont «arrêtées» (soit pour des raisons de blocage ou peut-être à cause d'une faille dans un protocole réseau qui serait moins évidente - ou d'autres raisons infinies), puis obtenir un vidage de pile à partir de Java où tous les threads sont appelés «Thread-1», «Thread-2», «Thread-3» n'est pas toujours très utile (cela dépend de la façon dont vos threads sont structuré et si vous pouvez dire lequel est juste par leur trace de pile - pas toujours possible si vous utilisez des groupes de plusieurs threads exécutant tous le même code).

Cela dit, vous pouvez bien sûr également faire ce qui précède de manière générique en créant une extension de la classe de threads qui définit son nom sur une trace de pile de son appel de création, puis l'utiliser avec vos implémentations Runnable au lieu de la classe de thread Java standard (voir ci-dessous) mais en plus de la trace de la pile, il pourrait y avoir plus d'informations spécifiques au contexte qui seraient utiles dans le nom du thread pour le débogage (une référence à l'une des nombreuses files d'attente ou sockets qu'il pourrait traiter, par exemple, auquel cas vous pourriez préférer étendez Thread spécifiquement pour ce cas afin que le compilateur vous force (ou que d'autres utilisent vos bibliothèques) à passer certaines informations (par exemple la file d'attente / socket en question) pour une utilisation dans le nom).

Voici un exemple de thread générique avec la trace de pile appelante comme nom:

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

et voici un échantillon de la sortie comparant les deux noms:

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]
Antonyme
la source
cHao quel est votre point? Vous ne pouviez pas utiliser votre code ci-dessus pendant l'exécution du thread pour obtenir une trace de pile de la création de threads (à la place, vous obtiendriez un nom simple ou au mieux une trace de pile du lancement des threads) mais en sous-classant le thread, vous pouvez faire exactement cela et forcer cela, même en exigeant des informations spécifiques au contexte, vous donnant ainsi une compréhension plus concrète de quel thread peut avoir un problème.
AntonyM
8
Mon point est que "Si vous implémentez Runnable, la classe qui implémente Runnable n'a aucun contrôle sur le nom du thread ..." est manifestement fausse. Une classe implémentant Runnablepeut en effet contrôler le nom du thread, car le thread exécutant le code est par définition le thread actuel (et tout code qui passe les contrôles de sécurité a le contrôle sur les noms des threads). Étant donné que vous consacrez la moitié de votre message à "omg, qu'en est-il des noms de threads!", Cela semble être un gros problème.
cHao
Le nom du fil? Rien ne vous empêche également d'étendre la classe de threads.
RichieHH
11

Exécutable car:

  • Laisse plus de flexibilité pour l'implémentation Runnable pour étendre une autre classe
  • Sépare le code de l'exécution
  • Vous permet d'exécuter votre exécutable à partir d'un pool de threads, du thread d'événement ou de toute autre manière à l'avenir.

Même si vous n'avez besoin de rien de tout cela maintenant, vous pourrez le faire à l'avenir. Puisqu'il n'y a aucun avantage à remplacer Thread, Runnable est une meilleure solution.

n13
la source
11

Étant donné que c'est un sujet très populaire et que les bonnes réponses sont réparties partout et traitées en profondeur, j'ai pensé qu'il était justifié de compiler les bonnes réponses des autres dans une forme plus concise, de sorte que les nouveaux arrivants ont un aperçu facile à l'avance:

  1. Vous étendez généralement une classe pour ajouter ou modifier des fonctionnalités. Donc, si vous ne souhaitez pas remplacer un comportement de thread , utilisez Runnable.

  2. De la même manière, si vous n'avez pas besoin d' hériter des méthodes de thread, vous pouvez vous passer de cette surcharge en utilisant Runnable.

  3. Héritage unique : si vous étendez Thread, vous ne pouvez pas l'étendre à partir d'une autre classe, donc si c'est ce que vous devez faire, vous devez utiliser Runnable.

  4. Il est judicieux de séparer la logique du domaine des moyens techniques, dans ce sens, il est préférable d'avoir une tâche exécutable isolant votre tâche de votre coureur .

  5. Vous pouvez exécuter le même objet Runnable plusieurs fois , un objet Thread, cependant, ne peut être démarré qu'une seule fois. (Peut-être la raison pour laquelle les exécuteurs acceptent les runnables, mais pas les threads.)

  6. Si vous développez votre tâche en tant que Runnable, vous avez toute la flexibilité nécessaire pour l'utiliser maintenant et à l'avenir . Vous pouvez le faire exécuter simultanément via Executors mais aussi via Thread. Et vous pouvez toujours l'utiliser / l'appeler de manière non simultanée dans le même thread, comme tout autre type / objet ordinaire.

  7. Cela facilite également la séparation des aspects de logique de tâche et de concurrence dans vos tests unitaires .

  8. Si vous êtes intéressé par cette question, vous pourriez également être intéressé par la différence entre Callable et Runnable .

Jörg
la source
@Pino Oui, Thread lui-même est également un exécutable. Cependant, si vous l'étendez pour simplement l'utiliser en tant que Runnable, quel est l'intérêt? Pourquoi ne pas simplement utiliser un Runnable ordinaire sans tous les bagages. Donc, je dirais que si vous étendez Thread, vous l'exécuterez également en utilisant sa méthode de démarrage, qui ne peut être utilisée qu'une seule fois. C'est le point que Nidhish-Krishnan voulait faire valoir dans sa réponse. Notez que la mienne n'est qu'une compilation ou un bref résumé d'autres réponses ici.
Jörg
11

Différence entre l'extension de thread et l'implémentation de Runnable:

entrez la description de l'image ici

Raman Gupta
la source
8

Ceci est abordé dans le didacticiel Oracle Defining and Starting a Thread :

Lequel de ces idiomes devez-vous utiliser? Le premier idiome, qui utilise un objet Runnable, est plus général, car l'objet Runnable peut sous-classer une classe autre que Thread. Le deuxième idiome est plus facile à utiliser dans les applications simples, mais est limité par le fait que votre classe de tâches doit être un descendant de Thread. Cette leçon se concentre sur la première approche, qui sépare la tâche exécutable de l'objet Thread qui exécute la tâche. Non seulement cette approche est plus flexible, mais elle est applicable aux API de gestion de threads de haut niveau abordées plus loin.

En d'autres termes, l'implémentation Runnablefonctionnera dans des scénarios où votre classe étend une classe autre que Thread. Java ne prend pas en charge l'héritage multiple. De plus, l'extension Threadne sera pas possible lors de l'utilisation de certaines API de gestion de threads de haut niveau. Le seul scénario où l'extension Threadest préférable est dans une petite application qui ne sera pas soumise à des mises à jour à l'avenir. Il est presque toujours préférable de l'implémenter Runnablecar il est plus flexible à mesure que votre projet se développe. Un changement de conception n'aura pas d'impact majeur car vous pouvez implémenter de nombreuses interfaces en java, mais étendre une seule classe.

Sionnach733
la source
8

L'explication la plus simple serait d'implémenter que Runnablenous pouvons affecter le même objet à plusieurs threads et que chacun Threadpartage les mêmes états et comportements d'objet.

Par exemple, supposons qu'il existe deux threads, thread1 place un entier dans un tableau et thread2 prend des entiers dans le tableau lorsque le tableau est rempli. Notez que pour que thread2 fonctionne, il doit connaître l'état du tableau, que thread1 l' ait rempli ou non.

L'implémentation Runnablevous permet d'avoir cette flexibilité pour partager l'objet tandis que extends Threadvous permet de créer de nouveaux objets pour chaque thread donc toute mise à jour effectuée par thread1 est perdue pour thread2.

Shababb Karim
la source
7

Si je ne me trompe pas, c'est plus ou moins semblable à

Quelle est la différence entre une interface et une classe abstraite?

étend établit la relation « est un » et l'interface fournit la capacité « a une ».

Préférer implémente Runnable :

  1. Si vous n'avez pas à étendre la classe Thread et modifier l'implémentation par défaut de l'API Thread
  2. Si vous exécutez une commande d'incendie et oubliez
  3. Si vous étendez déjà une autre classe

Préférez " étend le fil ":

  1. Si vous devez remplacer l'une de ces méthodes de thread comme indiqué dans la page de documentation d'Oracle

En règle générale, vous n'avez pas besoin de remplacer le comportement du thread. L' implémentation de Runnable est donc préférable la plupart du temps.

Sur une note différente, l'utilisation avancée ExecutorServiceou ThreadPoolExecutorServiceAPI offre plus de flexibilité et de contrôle.

Jetez un oeil à cette question SE:

ExecutorService vs Casual Thread Spawner

Ravindra babu
la source
5

La séparation de la classe Thread de l'implémentation Runnable évite également les problèmes de synchronisation potentiels entre le thread et la méthode run (). Un Runnable séparé donne généralement une plus grande flexibilité dans la façon dont le code exécutable est référencé et exécuté.

Govula Srinivas
la source
5

C'est le S de SOLID : responsabilité unique.

Un thread incarne le contexte d'exécution (comme dans le contexte d'exécution: cadre de pile, identifiant de thread, etc.) de l' exécution asynchrone d'un morceau de code. Idéalement, ce morceau de code devrait être la même implémentation, qu'elle soit synchrone ou asynchrone .

Si vous les regroupez dans une implémentation, vous attribuez à l'objet résultant deux causes de changement indépendantes :

  1. la gestion des threads dans votre application (ie. interroger et modifier le contexte d'exécution)
  2. algorithme implémenté par le morceau de code (la partie exécutable)

Si le langage que vous utilisez prend en charge les classes partielles ou l'héritage multiple, vous pouvez alors séparer chaque cause dans sa propre super classe, mais cela revient à la même chose que la composition des deux objets, car leurs ensembles de fonctionnalités ne se chevauchent pas. C'est pour la théorie.

Dans la pratique, d'une manière générale, un programme n'a pas besoin d'être plus complexe que nécessaire. Si vous avez un thread travaillant sur une tâche spécifique, sans jamais changer cette tâche, il est probablement inutile de faire des classes de tâches séparées, et votre code reste plus simple.

Dans le contexte de Java , puisque la fonction est déjà là , il est probablement plus facile de démarrer directement avec des Runnableclasses autonomes et de passer leurs instances à Thread(ou Executor) des instances. Une fois habitué à ce modèle, il n'est pas plus difficile à utiliser (ou même à lire) que le cas de thread exécutable simple.

didierc
la source
5

L'une des raisons pour lesquelles vous souhaitez implémenter une interface plutôt que d'étendre une classe de base est que vous étendez déjà une autre classe. Vous ne pouvez étendre qu'une seule classe, mais vous pouvez implémenter n'importe quel nombre d'interfaces.

Si vous étendez Thread, vous empêchez essentiellement que votre logique soit exécutée par un autre thread que «this». Si vous souhaitez uniquement un thread pour exécuter votre logique, il est préférable de simplement implémenter Runnable.

Nikhil AA
la source
Oui, en implémentant l'interface Runnable pour être libre d'implémenter votre propre logique en étendant n'importe quelle classe, c'est pourquoi Runnable est principalement préféré à la classe Thread.
Akash5288
5

si vous utilisez runnable, vous pouvez économiser de l'espace pour l'étendre à n'importe laquelle de vos autres classes.

user2771655
la source
5

Pouvons-nous revoir la raison fondamentale pour laquelle nous voulions que notre classe se comporte comme un Thread? Il n'y a aucune raison, nous voulions simplement exécuter une tâche, très probablement en mode asynchrone, ce qui signifie précisément que l'exécution de la tâche doit dériver de notre thread principal et du thread principal si elle se termine tôt, peut ou peut ne pas attendre pour le chemin ramifié (tâche).

Si tel est le but, alors où puis-je voir la nécessité d'un fil spécialisé. Cela peut être accompli en ramassant un thread RAW dans le pool de threads du système et en lui assignant notre tâche (peut être une instance de notre classe) et c'est tout.

Obéissons donc au concept des POO et écrivons une classe du type dont nous avons besoin. Il existe de nombreuses façons de faire les choses, le faire correctement compte.

Nous avons besoin d'une tâche, alors écrivez une définition de tâche qui peut être exécutée sur un thread. Utilisez donc Runnable.

Rappelez implements- vous toujours est spécialement utilisé pour conférer un comportement et extendsest utilisé pour conférer une caractéristique / propriété.

Nous ne voulons pas de la propriété du thread, mais nous voulons que notre classe se comporte comme une tâche qui peut être exécutée.

dharam
la source
4

Oui, si vous appelez un appel ThreadA, vous n'avez pas besoin d'appeler la méthode start et la méthode run est appelée uniquement après l'appel de la classe ThreadA. Mais si vous utilisez l'appel ThreadB, vous devez alors utiliser le thread de démarrage pour la méthode d'exécution des appels. Si vous avez plus d'aide, répondez-moi.

Manoj Kumar
la source
4

Je trouve qu'il est très utile d'utiliser Runnable pour toutes les raisons mentionnées, mais parfois j'aime étendre Thread pour pouvoir créer ma propre méthode d'arrêt de thread et l'appeler directement sur le thread que j'ai créé.

Tarvaris Jackson
la source
4

Java ne prend pas en charge l'héritage multiple, donc si vous étendez la classe Thread, aucune autre classe ne sera étendue.

Par exemple: si vous créez une applet, elle doit étendre la classe Applet, donc ici la seule façon de créer un thread est d'implémenter l'interface Runnable

Himanshu Mohta
la source
4

Runnableest une interface, tandis que Threadest une classe qui implémente cette interface. Du point de vue de la conception, il devrait y avoir une séparation nette entre la façon dont une tâche est définie et entre la façon dont elle est exécutée. Le premier est la responsabilité d'une Runnalbemise en œuvre, et le second est le travail de la Threadclasse. Dans la plupart des cas, la mise en œuvre Runnableest la bonne façon de suivre.

developer110
la source
4

Différence entre Thread et runnable. Si nous créons Thread en utilisant la classe Thread, alors Number of thread égal au nombre d'objet que nous avons créé. Si nous créons un thread en implémentant l'interface exécutable, nous pouvons utiliser un seul objet pour créer plusieurs threads. Ainsi, un seul objet est partagé par plusieurs Thread. Cela prendra donc moins de mémoire

Donc, selon l'exigence si nos données ne sont pas sensibles. Donc, il peut être partagé entre plusieurs threads, nous pouvons utiliser l'interface Runnable.

Rohit Chugh
la source
4

Ajouter mes deux cents ici - Toujours dans la mesure du possible implements Runnable. Voici deux mises en garde sur les raisons pour lesquelles vous ne devriez pas utiliser extends Threads

  1. Idéalement, vous ne devriez jamais étendre la classe Thread; le Threadcours doit être fait final. Au moins ses méthodes comme thread.getId(). Voir cette discussion pour un bogue lié à l'extension de Threads.

  2. Ceux qui aiment résoudre des énigmes peuvent voir un autre effet secondaire de l'extension du fil. Le code ci-dessous imprimera le code inaccessible lorsque personne ne les notifie.

Veuillez consulter 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 ");
        }
    } 
}
veritas
la source