À partir du 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
Existe-t-il une différence significative entre ces deux blocs de code?
Oui: implémente Runnable
est la méthode recommandée, IMO. Vous n'êtes pas vraiment spécialisé dans le comportement du fil. Vous lui donnez simplement quelque chose à courir. Cela signifie que composition est la manière philosophiquement "plus pure".
En termes pratiques , cela signifie que vous pouvez implémenter Runnable
et étendre également à partir d'une autre classe.
tl; dr: implémente Runnable is better. Cependant, la mise en garde est importante
En général, je vous recommanderais d'utiliser quelque chose comme Runnable
plutôt que Thread
car cela vous permet de garder votre travail uniquement associé de manière approximative à votre choix d'accès concurrentiel. Par exemple, si vous utilisez un Runnable
et décidez plus tard que cela n’exige pas en fait qu’il possède son propre Thread
, vous pouvez simplement appeler threadA.run ().
Avertissement: Par ici, je déconseille fortement l’utilisation de threads bruts. Je préfère de loin l'utilisation de Callables et FutureTasks (du javadoc: "Un calcul asynchrone annulable"). L'intégration des délais d'attente, l'annulation appropriée et le regroupement de threads du support de concurrence simultanée moderne me sont beaucoup plus utiles que des piles de threads bruts.
Suivi: il existe un constructeur FutureTask
qui vous permet d'utiliser Runnables (si c'est ce avec quoi vous êtes le plus à l'aise) et de continuer à en bénéficier. les outils de simultanéité modernes. Pour citer le javadoc :
Si vous n'avez pas besoin d'un résultat particulier, envisagez d'utiliser des constructions de la forme:
Future<?> f = new FutureTask<Object>(runnable, null)
Donc, si nous remplaçons leur runnable
par votre 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 afin d’exécuter "la tâche donnée ultérieurement."
Si vous souhaitez utiliser un pool de threads, le fragment de code ci-dessus deviendra le suivant (en utilisant la méthode de fabrique Executors.newCachedThreadPool () ):
ExecutorService es = Executors.newCachedThreadPool();
es.execute(new ThreadA());
Morale de l'histoire:
Hériter seulement si vous voulez remplacer un comportement.
Ou plutôt il devrait être lu comme:
Hériter moins, interface plus.
Eh bien tant de bonnes réponses, je veux ajouter plus à ce sujet. Cela aidera à comprendre Extending v/s Implementing Thread
.
Extends lie très étroitement deux fichiers de classe et peut rendre difficile la gestion du code.
Les deux approches font le même travail mais il y a eu quelques différences.
La différence la plus courante est la suivante:
Cependant, un différence significative entre l'implémentation de Runnable et l'extension de Thread est queby 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 à comprendre plus clairement
//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 d'interface Runnable, 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 de counter est incrémentée pour chaque accès au thread.
Considérant que, dans 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'aucune incrémentation ne sera effectuée car aucune des références d'objet n'est identique.
Quand utiliser Runnable?
Utilisez l’interface Runnable 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 de plusieurs objets consomme plus de mémoire et entraîne de lourdes pertes de temps.
Une classe qui implémente Runnable n'est pas un thread, mais simplement une classe. Pour qu'un Runnable devienne un Thread, vous devez créer une instance de Thread et se transmettre lui-même en tant que cible.
Dans la plupart des cas, l'interface Runnable doit être utilisée si vous prévoyez uniquement de remplacer la méthode run()
et aucune autre méthode Thread. Cela 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 son comportement fondamental.
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 en implémentant l'interface Runnable pour créer un thread.
J'espère que cela aidera!
Une chose qui, je l’ai surprise, n’a pas encore été mentionnée, est que la mise en oeuvre de Runnable
rend votre classe plus flexible.
Si vous prolongez le fil alors l'action que vous faites sera toujours dans un fil. Cependant, si vous implémentez Runnable
, ce n'est pas forcément le cas. Vous pouvez l'exécuter dans un thread, ou le transmettre à un type de service exécuteur, ou simplement le transmettre en tant que tâche dans une application à thread unique (peut-être pour être exécuté ultérieurement, mais dans le même thread). Les options sont beaucoup plus ouvertes si vous utilisez simplement Runnable
que si vous vous liez à Thread
.
Si vous voulez implémenter ou étendre une autre classe, alors l'interface Runnable
est préférable, sinon, si vous ne voulez pas qu'une autre classe étende ou implémente, alors la classe Thread
est préférable.
La différence la plus fréquente est
Lorsque vous extends Thread
classe, vous ne pouvez pas étendre la classe requise. (Comme vous le savez, Java n'autorise pas l'héritage de plusieurs classes).
Lorsque vous implements Runnable
, vous pouvez économiser de l'espace pour que votre classe puisse étendre toute autre classe à l'avenir ou maintenant.
Java ne prend pas en charge les héritages multiples, ce qui signifie que vous ne pouvez étendre qu'une classe dans Java. Ainsi, une fois que vous avez étendu la classe Thread, vous avez perdu votre chance et vous ne pouvez pas étendre ou hériter d'une autre classe en Java.
Dans la programmation orientée objet, étendre une classe signifie généralement ajouter de nouvelles fonctionnalités et modifier ou améliorer les comportements. Si nous ne modifions pas Thread, utilisez plutôt l'interface Runnable.
L'interface exécutable représente une tâche pouvant être exécutée par un thread ou des exécuteurs ordinaires, ou par tout autre moyen. La séparation logique de Task en tant que Runnable que Thread est une bonne décision de conception.
Séparer la tâche en tant que Runnable signifie que nous pouvons la réutiliser et qu’il est également libre de l’exécuter de différentes manières. puisque vous ne pouvez pas redémarrer un thread une fois qu'il est terminé. encore une fois Runnable vs Thread pour la tâche, Runnable est le gagnant.
Le concepteur Java le reconnaît et c’est la raison pour laquelle les exécuteurs acceptent Runnable en tant que tâche et ils ont un thread de travail qui exécute cette tâche.
L'héritage de toutes les méthodes de threads représente un temps système supplémentaire, juste pour représenter une tâche, ce qui peut être fait facilement avec Runnable.
Courtoisie de javarevisited.blogspot.com
Celles-ci étaient parmi les différences notables entre Thread et Runnable en Java. Si vous connaissez d’autres différences entre Thread et Runnable que celles-ci, veuillez les partager via des commentaires. Personnellement, j'utilise Runnable over Thread pour ce scénario et recommande d'utiliser une interface Runnable ou Callable en fonction de vos besoins.
Cependant, la différence significative est.
Lorsque vous extends Thread
classe, chacun de vos threads crée un objet unique et l’associe à celui-ci. Lorsque vous implements Runnable
, il partage le même objet avec plusieurs threads.
En fait, il n'est pas sage de comparer Runnable
et Thread
l'un avec l'autre.
Ces deux ont une dépendance et une relation en multi-threading tout comme la relation _Wheel and Engine
_ du véhicule à moteur.
Je dirais qu’il n’ya qu’une seule façon de faire du multi-threading en deux étapes. Laissez-moi faire mon point.
Runnable:
Lorsque vous implémentez _interface Runnable
_, cela signifie que vous créez quelque chose qui est _run able
_ dans un autre thread. Maintenant, créer quelque chose qui peut s’exécuter dans un thread (s’exécuter dans un thread) ne signifie pas créer un thread.
La classe MyRunnable
n'est donc qu'une classe ordinaire avec une méthode _void run
_. Et ses objets seront des objets ordinaires avec seulement une méthode run
qui s'exécutera normalement à l'appel. (sauf si nous passons l'objet dans un fil).
Fil:
_class Thread
_, je dirais Une classe très spéciale avec la possibilité de démarrer un nouveau thread qui permet en réalité le multi-threading via sa méthode start()
.
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:
Donc techniquement et théoriquement, les deux sont nécessaires pour démarrer un thread, on va courir et on va le faire courir (Comme Wheel and Engine
= de véhicule à moteur).
C'est pourquoi vous ne pouvez pas démarrer un thread avec MyRunnable
vous devez le passer à une instance de Thread
.
Mais il est possible de créer et d'exécuter un thread uniquement à l'aide de _class Thread
_ parce que la classe Thread
implémente Runnable
pour que nous sachions tous que Thread
est également un Runnable
à l'intérieur.
Enfin Thread
et Runnable
sont complémentaires pour le multithreading, ni concurrent ni remplacement.
Vous devez implémenter Runnable, mais si vous utilisez Java 5 ou une version ultérieure, ne le démarrez pas avec new Thread
, mais utilisez plutôt ExecutorService . Pour plus de détails, voir: Comment implémenter un threading simple en Java .
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, de sorte que vous ne pouvez étendre qu'une 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 dans un sens ou dans l’autre, c’est donc faux.
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();
Ceci est peut-être un peu influencé par mon utilisation récente de Javascript et d'Actionscript 3, mais de cette façon, votre classe n'aura pas besoin d'implémenter une interface assez vague comme Runnable
.
Avec la sortie de Java 8, il existe maintenant une troisième option.
Runnable
est un interface fonctionnelle , ce qui signifie que des instances de celui-ci peuvent être créées avec des expressions lambda ou des références de méthodes.
Votre exemple peut être remplacé par:
new Thread(() -> { /* Code here */ }).start()
ou si vous voulez utiliser un ExecutorService
et une référence de méthode:
executor.execute(runner::run)
Celles-ci sont non seulement beaucoup plus courtes que vos exemples, mais comportent également de nombreux avantages énoncés dans d'autres réponses, à savoir utiliser Runnable
sur Thread
, telles que la responsabilité unique et l'utilisation de la composition, car vous ne spécialisez pas le comportement du fil. Cela évite également de créer une classe supplémentaire si tout ce dont vous avez besoin est un Runnable
comme vous le faites dans vos exemples.
L'instanciation d'une interface donne une séparation plus nette entre votre code et l'implémentation de threads, je préférerais donc implémenter Runnable dans ce cas.
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 également de bonnes raisons d'é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 suit:
new Thread(myRunnable,"WhateverNameiFeelLike");
mais si vous étendez Thread, vous devez gérer cela au sein de la classe elle-même (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) nous obligeons à utiliser ce nom pour toutes les instances de cette classe (à moins que vous ignoriez le fait qu'il s'agisse d'un thread et que vous fassiez ce qui précède avec lui comme s'il s'agissait d'un Runnable, mais nous parlons ici de convention ignorer cette possibilité je sens).
Vous pouvez même, par exemple, prendre une trace de pile de sa création et l’utiliser comme nom de fil. Cela peut sembler étrange, mais selon la structure de votre code, cela peut s'avérer très utile pour le débogage.
Cela peut sembler une petite chose, mais si vous avez une application très complexe avec beaucoup de threads et tout à coup, les choses "se sont arrêtées" (soit pour des raisons de blocage, soit peut-être à cause d'une faille dans un protocole réseau qui serait moins efficace). évident - ou d’autres raisons sans fin) puis obtenir un dump de pile de Java où tous les threads sont appelés "Thread-1", "Thread-2", "Thread-3" n’est pas toujours très utile dépend de la façon dont vos threads sont structurés et de la possibilité de savoir avec certitude lequel est ce qui n’est que par leur trace de pile - ce n’est 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 procéder de manière générique à ce qui précède en créant une extension de la classe de thread qui définit son nom comme une trace de pile de son appel de création, puis l’utilisez avec vos implémentations Runnable à la place de la norme Java Classe de thread (voir ci-dessous), mais en plus de la trace de 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 pouvant être traités, par exemple). Dans ce cas, vous préférerez peut-être étendre Thread spécifiquement à ce cas afin que le compilateur vous oblige (ou utilise d'autres bibliothèques utilisant vos bibliothèques) à transmettre certaines informations (par exemple, la file d'attente/le socket en question, à utiliser dans le nom).
Voici un exemple de thread générique ayant pour nom la trace de pile appelante:
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 exemple 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]
Runnable parce que:
Même si vous n'en avez pas besoin maintenant, vous pouvez le faire à l'avenir. Comme il n’ya aucun avantage à remplacer Thread, Runnable est une meilleure solution.
Comme il s'agit d'un sujet très populaire et que les bonnes réponses sont répandues et traitées de manière approfondie, j'ai estimé qu'il était justifié de compiler les bonnes réponses des autres dans un format plus concis, afin que les nouveaux arrivants aient un aperçu rapide dès le départ:
Vous étendez généralement une classe pour ajouter ou modifier des fonctionnalités. Donc, si vous ne voulez pas à écraser tout Comportement du fil, utilisez alors Runnable.
De la même façon, si vous n'avez pas besoin de pour hériter des méthodes de thread, vous pouvez le faire sans overhead en utilisant Runnable.
Single héritance: Si vous étendez Thread, vous ne pouvez pas étendre à partir d'une autre classe. Si c'est ce que vous devez faire, vous devez utiliser Runnable.
Il est judicieux de séparer la logique de domaine des moyens techniques. En ce sens, il est préférable d’avoir une tâche exécutable isolement votre tâche de votre coureur .
Vous pouvez exécuter le même Runnable objet plusieurs fois, un objet Thread ne peut toutefois être démarré qu'une seule fois. (Peut-être que la raison pour laquelle les exécuteurs acceptent les Runnables, mais pas les Threads.)
Si vous développez votre tâche en tant que Runnable, vous avez toute souplesse pour l’utiliser maintenant et à l’avenir. Vous pouvez le faire fonctionner simultanément via Executors mais également via Thread. Et vous pouvez toujours aussi utiliser/l'appeler de manière non concurrente dans le même thread, comme n'importe quel autre type/objet ordinaire.
Cela rend également plus facile à séparer la logique de tâche et la concurrence aspects dans vos tests unitaires.
Si cette question vous intéresse, vous pourriez être également intéressé par le différence entre Callable et Runnable .
Ceci est décrit dans le tutoriel Définition et démarrage d'un fil d'Oracle:
Lequel de ces idiomes devriez-vous utiliser? Le premier langage, 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 langage est plus facile à utiliser dans les applications simples, mais il 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 pouvant être exécutée de l'objet Thread qui l'exécute. Non seulement cette approche est-elle plus flexible, mais elle est applicable aux API de gestion de threads de haut niveau abordées plus tard.
En d'autres termes, l'implémentation de Runnable
fonctionnera dans les 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 de Thread
ne sera pas possible avec certaines des API de gestion de threads de haut niveau. Le seul scénario dans lequel il est préférable d'étendre Thread
concerne une petite application qui ne fera pas l'objet de mises à jour à l'avenir. Il est presque toujours préférable d'implémenter Runnable
car il est plus flexible à mesure que votre projet se développe. Une modification de conception n'aura pas d'impact majeur car vous pouvez implémenter de nombreuses interfaces en Java, mais seulement étendre une classe.
Si je ne me trompe pas, c'est plus ou moins semblable à
Quelle est la différence entre une classe d'interface et une classe abstraite?
étend établit " est un " relation & interface fournit " a un " aptitude.
Prefer implémente Runnable :
Préférer " étend le fil ":
Généralement, vous n'avez pas besoin de remplacer le comportement du thread. Donc implémente Runnable est préféré pour la plupart du temps.
Sur une note différente, l'utilisation avancée de l'API ExecutorService
ou ThreadPoolExecutorService
offre davantage de flexibilité et de contrôle.
Regardez cette question SE:
L'explication la plus simple consisterait à implémenter Runnable
, nous pouvons affecter le même objet à plusieurs threads et chaque Thread
partage les mêmes états d'objet et le même comportement.
Par exemple, supposons qu'il y ait deux threads, thread1 met 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 de Runnable
vous permet de partager l'objet avec cette souplesse alors que extends Thread
vous permet de créer de nouveaux objets pour chaque thread. Par conséquent, toute mise à jour effectuée par thread1 est perdue pour thread2.
La séparation de la classe Thread de l'implémentation Runnable évite également d'éventuels problèmes de synchronisation entre le thread et la méthode run (). Un Runnable séparé offre généralement une plus grande flexibilité dans la manière dont le code exécutable est référencé et exécuté.
C'est le S de SOLIDE : responsabilité unique.
Un thread représente 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. Ce morceau de code devrait idéalement être la même implémentation, que ce soit synchrone ou asynchrone .
Si vous les regroupez dans une seule implémentation, vous donnez à l'objet résultant deux causes de changement non liées :
Si le langage que vous utilisez prend en charge les classes partielles ou l'héritage multiple, vous pouvez séparer chaque cause dans sa propre super classe, mais cela revient au même que la composition des deux objets, car leurs ensembles de fonctionnalités ne se chevauchent pas. C'est pour la théorie.
En pratique, de manière générale, un programme n'a pas besoin de comporter plus de complexité que nécessaire. Si vous avez un thread travaillant sur une tâche spécifique, sans jamais modifier cette tâche, il est probablement inutile de séparer les tâches des tâches 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 classes Runnable
autonomes et transmettez leurs instances à des instances Thread
(ou Executor
). Une fois utilisé pour ce motif, il n’est pas plus difficile à utiliser (ni même à lire) que le simple cas de thread exécutable.
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 classe, mais vous pouvez implémenter un nombre illimité d'interfaces.
Si vous étendez Thread, vous empêchez en principe que votre logique soit exécutée par un autre thread que 'this'. Si vous ne voulez que certains threads exécutent votre logique, il est préférable de simplement implémenter Runnable.
Pouvons-nous revenir sur la raison fondamentale pour laquelle nous voulions que notre classe se comporte comme un Thread
? Il n'y a aucune raison du tout, nous voulions simplement exécuter une tâche, 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 le thread principal, s'il se termine tôt, peut attendre ou ne pas attendre. pour le chemin de branchement (tâche).
Si tel est le but, où puis-je voir la nécessité d'un fil spécialisé. Cela peut être accompli en choisissant un thread RAW dans le pool de threads du système et en lui attribuant notre tâche (pouvant être une instance de notre classe), et c'est tout.
Alors, obéissons au concept de POO et écrivons une classe du type dont nous avons besoin. Il y a plusieurs façons de faire les choses, le faire de la bonne façon compte.
Nous avons besoin d'une tâche. Écrivez donc une définition de tâche pouvant être exécutée sur un thread. Alors utilisez Runnable.
Rappelez-vous toujours que implements
est spécialement utilisé pour conférer un comportement et que extends
est utilisé pour conférer une caractéristique/propriété.
Nous ne voulons pas de la propriété du thread, nous voulons plutôt que notre classe se comporte comme une tâche pouvant être exécutée.
si vous utilisez runnable, vous pouvez économiser de l'espace pour pouvoir l'étendre à n'importe laquelle de vos autres classes.
Une différence entre l'implémentation de Runnable et l'extension de thread est que, en étendant Thread, chacun de vos threads est associé à un objet unique, alors que l'implémentation de Runnable permet à plusieurs threads de partager la même instance d'objet.
Une classe qui implémente Runnable n'est pas un thread, mais simplement une classe. Pour qu'un Runnable soit exécuté par un Thread, vous devez créer une instance de Thread et passer l'instance Runnable comme cible.
Dans la plupart des cas, l'interface Runnable doit être utilisée si vous prévoyez uniquement de remplacer la méthode run () et aucune autre méthode Thread. Cela 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 son comportement fondamental.
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 en implémentant l'interface Runnable pour créer un thread. Mais si nous étendons simplement la classe Thread, nous ne pouvons hériter d'aucune autre classe.
Je trouve qu'il est très utile d'utiliser Runnable pour toutes les raisons mentionnées, mais j'aime parfois étendre Thread afin de pouvoir créer ma propre méthode d'arrêt de thread et l'appeler directement sur le thread que j'ai créé.
Le meilleur moyen pour la plupart des threads de travail consiste à encapsuler complètement le thread dans la classe de travail afin que rien ne puisse interférer de l'extérieur et provoquer des états de thread/classe non désirés et non valides.
Je viens de poster un exemple, je vais donc également le partager avec vous:
/**
* This worker can only run once
* @author JayC667
*/
public class ProperThreading {
private final Thread mThread = new Thread(() -> runWorkingLoop()); // if you want worker to be able to run multiple times, move initialisation into startThread()
private volatile boolean mThreadStarted = false;
private volatile boolean mStopRequested = false;
private final long mLoopSleepTime;
public ProperThreading(final long pLoopSleepTime /* pass more arguments here, store in members */ ) {
mLoopSleepTime = pLoopSleepTime;
}
public synchronized void startThread() {
if (mThreadStarted) throw new IllegalStateException("Worker Thread may only be started once and is already running!");
mThreadStarted = true;
mThread.start();
}
private void runWorkingLoop() {
while (!mStopRequested /* && other checks */ ) {
try {
// do the magic work here
Thread.sleep(mLoopSleepTime);
} catch (final InterruptedException e) {
break;
} catch (final Exception e) {
// do at least some basic handling here, you should NEVER ignore exception unless you know exactly what you're doing, and then it should be commented!
}
}
}
public synchronized void stopThread() {
if (!mThreadStarted) throw new IllegalStateException("Worker Thread is not even running yet!");
mStopRequested = true;
mThread.interrupt();
}
}
Runnable
est une interface, tandis que Thread
est une classe qui implémente cette interface. Du point de vue de la conception, il devrait exister une séparation nette entre la définition d’une tâche et son exécution. Le premier est la responsabilité d'une implémentation Runnalbe
, et le dernier est le travail de la classe Thread
. Dans la plupart des cas, implémenter Runnable
est la bonne façon de le suivre.
Différence entre Thread et runnable. Si nous créons un thread en utilisant la classe Thread, alors le nombre de threads est égal au nombre d'objets que nous avons créés. Si nous créons un thread en implémentant l'interface exécutable, nous pouvons utiliser un seul objet pour créer plusieurs threads. Un seul objet est partagé par plusieurs threads. Il faudra donc moins de mémoire.
Donc, en fonction des besoins, si nos données ne sont pas sensibles. Donc, il peut être partagé entre plusieurs threads, nous pouvons utiliser l'interface Runnable.
Ajouter mes deux sous ici - Toujours utiliser, chaque fois que possible, implements Runnable
. Vous trouverez ci-dessous deux mises en garde vous invitant à ne pas utiliser extends Thread
s
Idéalement, vous ne devriez jamais étendre la classe Thread. la classe Thread
doit être rendue final
. Au moins ses méthodes comme thread.getId()
. Voir this discussion pour un bogue lié à l'extension de Thread
s.
Ceux qui aiment résoudre des énigmes peuvent voir un autre effet secondaire de l'extension de Thread. Le code ci-dessous imprimera du code inaccessible lorsque personne ne le leur notifiera.
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 ");
}
}
}
Java ne supporte pas 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. Ici, la seule façon de créer un thread consiste à implémenter l'interface Runnable.
Oui, si vous appelez un appel ThreadA, il n'est pas nécessaire d'appeler la méthode start et la méthode run est un appel après l'appel de la classe ThreadA uniquement. Mais si vous utilisez l'appel ThreadB, vous devez utiliser le thread de démarrage pour la méthode d'appel. Si vous avez plus d'aide, répondez-moi.
Le fil contient un comportement auquel il n'est pas destiné d'accéder;
cependant, si vous avez une sous-classe Thread, vous devez considérer que plus de thread est implémenté.
public class ThreadMain {
public int getId() {
return 12345678;
}
public String getName() {
return "Hello World";
}
public String getState() {
return "testing";
}
public void example() {
new Thread() {
@Override
public void run() {
System.out.println("id: "+getId()+", name: "+getName()+", state: "+getState());
}
}.start();
}
public static void main(String[] args) {
new ThreadMain().example();
}
}
Si vous exécutez cela, vous pouvez vous attendre
id: 12345678, name: Hello World, state: testing
cependant, vous n'appelez pas les méthodes que vous pensez utiliser car vous utilisez la méthode dans Thread
pas ThreadMain
et vous voyez plutôt quelque chose comme
id: 11, name: Thread-0, state: RUNNABLE
Dans les rares cas où vous ne l'exécutez qu'une seule fois, vous devez étendre Thread à cause de DRY. Si vous l'appelez plusieurs fois, vous devez implémenter Runnable car le même thread ne doit pas être redémarré.
En étendant la classe de thread, la classe dérivée ne peut étendre aucune autre classe de base car Java n'autorise qu'un seul héritage. au contraire, en implémentant l'interface exécutable, la classe étend toujours une autre classe de base.
La différence la plus significative entre l'implémentation de Runnable et l'extension de thread est donnée ci-dessous:
En étendant Thread, la classe dérivée est elle-même un objet thread, alors que l’implémentation de l’interface Runnable partage le même objet avec plusieurs threads.
Ce n'est peut-être pas une réponse, mais quand même; il existe un autre moyen de créer des threads:
Thread t = new Thread() {
public void run() {
// Code here
}
}
1. Étendre l'interface de thread, c'est comme si vous faisiez en sorte que votre classe ne se comporte que comme un thread. Votre nouvelle classe ressemblera à un thread amélioré.
jshell> public class Test extends Thread{
...> public Test(String name){
...> super(name);
...> }
...> public void run(){
...> System.out.println(Thread.currentThread().getName());
...> }
...> }
| created class Test
jshell> Test t1=new Test("MyThread");
t1 ==> Thread[MyThread,5,main]
Il crée un fil, pas l'objet Test. Donc, ça va agir comme un seul fil. Vous ne pouvez pas partager l'instance de la classe Test entre les threads.
2. Implémentation de l'interface exécutable.
jshell> public class Test1 implements Runnable{
...> public void run(){
...> System.out.println(Thread.currentThread().getName());
...> }
...> public String getName(){
...> return "testing";}
...> }
| created class Test1
jshell> Test1 t1=new Test1();
t1 ==> Test1@396a51ab --> this creates Test1 object.
Cet objet peut être partagé entre les threads par,
jshell> Thread t1=new Thread(t1,"Hai");
t ==> Thread[Hai,5,main]
jshell> Thread t=new Thread(t1,"Hai");
t ==> Thread[Hai,5,main]
Je pense que beaucoup a déjà été discuté sur ce sujet. bien que cela puisse être utile dans les bases.
Voici un moyen simple de dire: si vous implémentez une interface, vous implémentez toutes ses méthodes et si vous étendez la classe, vous héritez d'une méthode de votre choix ... Dans ce cas, il y a ne seule méthode nommée Run () so préférable d'implémenter l'interface Runnable ..
La classe Thread définit plusieurs méthodes pouvant être overriden
par la classe extensible. Mais pour créer un fil, nous devons écraser la méthode run()
. Il en va de même pour Runnable
.
Cependant, Runnable
est une méthode recommandée pour créer un thread. Les principales raisons sont:
Runnable étant une interface, vous pouvez étendre d'autres classes. Mais si vous étendez Thread, cette option disparaîtra.
Si vous ne modifiez pas ou n'améliore pas beaucoup de fonctionnalités Thread
et étendez la classe Thread
n'est pas une méthode recommandée.
La principale différence entre Thread et Runnable est la suivante: - Thread est similaire à: Worker (execute Runnable) - Runnable est similaire à: Job (à exécuter par Thread)
Je dirais que la tâche réelle est découplée du fil. Nous pouvons transmettre la tâche à Thread, à la structure Executor, etc. dans le cas de Runnable, alors que l'extension de la tâche Thread est couplée à l'objet thread lui-même. L'isolation de la tâche ne peut pas être effectuée en cas d'extension du thread. C'est comme si nous gravions la tâche sur un objet Thread, par exemple une puce électronique (et plus précisément, nous ne recevrons aucune poignée de tâche).