Seit meiner Zeit mit Threads in Java habe ich zwei Möglichkeiten gefunden, Threads zu schreiben:
Mit implements Runnable
:
public class MyRunnable implements Runnable {
public void run() {
//Code
}
}
//Started with a "new Thread(new MyRunnable()).start()" call
Oder mit extends Thread
:
public class MyThread extends Thread {
public MyThread() {
super("MyThread");
}
public void run() {
//Code
}
}
//Started with a "new MyThread().start()" call
Gibt es einen signifikanten Unterschied zwischen diesen beiden Codeblöcken?
java
multithreading
runnable
implements
java-threads
user65374
quelle
quelle
interrupt()
. Auch hier ist es eine Idee, es könnte im richtigen Fall nützlich sein, aber ich empfehle es nicht.Antworten:
Ja, Geräte
Runnable
sind der bevorzugte Weg, IMO. Sie sind nicht wirklich auf das Verhalten des Threads spezialisiert. Du gibst ihm nur etwas zum Laufen. Das heißt, Komposition ist der philosophisch "reinere" Weg.In der Praxis bedeutet dies, dass Sie auch eine
Runnable
andere Klasse implementieren und erweitern können .quelle
if (numberCores > 4) myExecutor.excute(myRunnable); else myRunnable.run()
extends Thread
und wenn Sie kein Threading möchten, warum würden Sie überhaupt implementierenRunnable
...tl; dr: implementiert Runnable ist besser. Die Einschränkung ist jedoch wichtig
Im Allgemeinen würde ich empfehlen, so etwas wie zu verwenden,
Runnable
anstattThread
weil es Ihnen ermöglicht, Ihre Arbeit nur lose mit Ihrer Wahl der Parallelität zu verbinden. Wenn Sie beispielsweise a verwendenRunnable
und später entscheiden, dass dies tatsächlich kein eigenes erfordertThread
, können Sie einfach threadA.run () aufrufen.Vorsichtsmaßnahme: Ich rate hier dringend von der Verwendung von Rohfäden ab. Ich bevorzuge die Verwendung von Callables und FutureTasks (aus dem Javadoc: "Eine stornierbare asynchrone Berechnung"). Die Integration von Zeitüberschreitungen, das ordnungsgemäße Abbrechen und das Thread-Pooling der modernen Parallelitätsunterstützung sind für mich viel nützlicher als Stapel von rohen Threads.
Follow-up: Es gibt einen
FutureTask
Konstruktor , mit dem Sie Runnables verwenden können (wenn Sie damit am besten vertraut sind) und dennoch die Vorteile der modernen Parallelitätstools nutzen können. Um den Javadoc zu zitieren :Wenn Sie kein bestimmtes Ergebnis benötigen, sollten Sie Konstruktionen des Formulars verwenden:
Also, wenn wir ihre ersetzen
runnable
mit IhremthreadA
, erhalten wir folgendes:Eine weitere Option, mit der Sie näher an Runnables bleiben können, ist ein ThreadPoolExecutor . Sie können die Verwendung ausführen Methode in einem Runnable passieren auszuführen „um die gegebene Aufgabe irgendwann in der Zukunft.“
Wenn Sie versuchen möchten, einen Thread-Pool zu verwenden, sieht das obige Codefragment folgendermaßen aus (mithilfe der Factory-Methode Executors.newCachedThreadPool () ):
quelle
es
wäre besser als statisches (oder injiziertes) Feld, so dass es nur einmal erstellt wird.FutureTask
direkt damit umzugehen ist im Allgemeinen nicht das, was Sie tun möchten.ExecutorService
s erstellt dasFuture
für Sie passende , wenn Siesubmit
einRunnable
/Callable
zu ihnen. Ebenso fürScheduledExecutorService
s undScheduledFuture
wenn Sieschedule
einRunnable
/Callable
.Moral der Geschichte:
Erben Sie nur, wenn Sie ein bestimmtes Verhalten überschreiben möchten.
Oder besser gesagt sollte es gelesen werden als:
Weniger erben, mehr Schnittstelle.
quelle
run()
Methode überschreiben .java.lang.Thread
indem Sie dierun()
Methode überschreiben . In diesem Fall müssen Sie diestart()
Methode überschreiben , denke ich. Normalerweise verwenden Sie das Verhalten von einfach wieder,java.lang.Thread
indem Sie Ihren Ausführungsblock in dierun()
Methode einfügen.Nun, so viele gute Antworten, ich möchte mehr dazu hinzufügen. Dies wird zum Verständnis beitragen
Extending v/s Implementing Thread
.Extends bindet zwei Klassendateien sehr eng und kann dazu führen, dass Code nur schwer zu verarbeiten ist.
Beide Ansätze machen den gleichen Job, aber es gab einige Unterschiede.
Der häufigste Unterschied ist
Ein wesentlicher Unterschied zwischen der Implementierung von Runnable und der Erweiterung von Thread besteht jedoch darin, dass
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.
Das folgende Beispiel hilft Ihnen, es besser zu verstehen
Ausgabe des obigen Programms.
Beim Runnable-Schnittstellenansatz wird nur eine Instanz einer Klasse erstellt und von verschiedenen Threads gemeinsam genutzt. Der Wert des Zählers wird also für jeden Thread-Zugriff erhöht.
Während beim Thread-Klassenansatz müssen Sie für jeden Thread-Zugriff eine separate Instanz erstellen. Daher wird für jede Klasseninstanz ein anderer Speicher zugewiesen und jede hat einen separaten Zähler. Der Wert bleibt gleich, was bedeutet, dass kein Inkrement erfolgt, da keine der Objektreferenzen gleich ist.
Wann soll Runnable verwendet werden?
Verwenden Sie die ausführbare Schnittstelle, wenn Sie über die Gruppe von Threads auf dieselben Ressourcen zugreifen möchten. Vermeiden Sie hier die Verwendung der Thread-Klasse, da die Erstellung mehrerer Objekte mehr Speicherplatz beansprucht und ein hoher Leistungsaufwand entsteht.
Eine Klasse, die Runnable implementiert, ist kein Thread und nur eine Klasse. Damit ein Runnable zu einem Thread wird, müssen Sie eine Thread-Instanz erstellen und sich selbst als Ziel übergeben.
In den meisten Fällen sollte die Runnable-Schnittstelle verwendet werden, wenn Sie nur die
run()
Methode und keine anderen Thread-Methoden überschreiben möchten . Dies ist wichtig, da Klassen nur dann in Unterklassen unterteilt werden sollten, wenn der Programmierer beabsichtigt, das grundlegende Verhalten der Klasse zu ändern oder zu verbessern.Wenn eine Oberklasse erweitert werden muss, ist die Implementierung der Runnable-Schnittstelle besser geeignet als die Verwendung der Thread-Klasse. Weil wir eine andere Klasse erweitern können, während wir die Runnable-Schnittstelle implementieren, um einen Thread zu erstellen.
Ich hoffe das wird helfen!
quelle
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();
Ist es klarer?Thread
die Aussageby extending Thread, each of your threads has a unique object associated with it, whereas implementing Runnable, many threads can share the same object instance
dann falsch , da Ihre Version des Codes ebenfalls inkrementell ist ? Wenn nicht, was ist dann ein Fall, der dies demonstriert?Eine Sache, von der ich überrascht bin, dass sie noch nicht erwähnt wurde, ist, dass die Implementierung
Runnable
Ihre Klasse flexibler macht.Wenn Sie den Thread erweitern, befindet sich die Aktion, die Sie ausführen, immer in einem Thread. Wenn Sie jedoch implementieren
Runnable
, muss es nicht sein. Sie können es in einem Thread ausführen oder an eine Art Executor-Service übergeben oder es einfach als Aufgabe in einer einzelnen Thread-Anwendung weitergeben (möglicherweise zu einem späteren Zeitpunkt, aber innerhalb desselben Threads). Die Optionen sind viel offener, wenn Sie sie nur verwenden,Runnable
als wenn Sie sich daran bindenThread
.quelle
Thread
Objekt tun, weilThread implements Runnable
... ;-) Aber es "fühlt sich besser an", diese Dinge mit einem zuRunnable
tun, als sie mit einem zu tunThread
!Thread
fügt eine Menge zusätzlicher Dinge hinzu, die Sie nicht benötigen und in vielen Fällen nicht wollen. Sie sind immer besser dran, die Schnittstelle zu implementieren, die Ihren tatsächlichen Aktivitäten entspricht.Wenn Sie eine andere Klasse implementieren oder erweitern möchten,
Runnable
ist die Schnittstelle am besten vorzuziehen. Wenn Sie nicht möchten, dass eine andere Klasse erweitert oder implementiert wird, ist dieThread
Klasse vorzuziehen.Der häufigste Unterschied ist
Wenn Sie unterrichten
extends Thread
, können Sie danach keine andere Klasse erweitern, die Sie benötigt haben. (Wie Sie wissen, erlaubt Java nicht, mehr als eine Klasse zu erben.)Wenn Sie
implements Runnable
möchten, können Sie Platz für Ihre Klasse sparen, um jede andere Klasse in Zukunft oder jetzt zu erweitern.Java unterstützt nicht mehrere Vererbungen. Dies bedeutet, dass Sie nur eine Klasse in Java erweitern können. Wenn Sie also die Thread-Klasse erweitert haben, haben Sie Ihre Chance verloren und können keine andere Klasse in Java erweitern oder erben.
In der objektorientierten Programmierung bedeutet das Erweitern einer Klasse im Allgemeinen das Hinzufügen neuer Funktionen und das Ändern oder Verbessern von Verhaltensweisen. Wenn wir keine Änderungen an Thread vornehmen, verwenden Sie stattdessen die Runnable-Schnittstelle.
Die ausführbare Schnittstelle stellt eine Aufgabe dar, die entweder von einem einfachen Thread oder von Executors oder auf andere Weise ausgeführt werden kann. Daher ist die logische Trennung von Task als ausführbar als Thread eine gute Entwurfsentscheidung.
Wenn Sie die Aufgabe als ausführbar trennen, können Sie die Aufgabe wiederverwenden und haben auch die Freiheit, sie auf verschiedene Weise auszuführen. da Sie einen Thread nach Abschluss nicht mehr neu starten können. wieder Runnable vs Thread für Aufgabe, Runnable ist Gewinner.
Der Java-Designer erkennt dies und deshalb akzeptieren Executoren Runnable als Task und haben einen Worker-Thread, der diese Task ausführt.
Das Erben aller Thread-Methoden ist ein zusätzlicher Aufwand, nur um eine Aufgabe darzustellen, die mit Runnable problemlos ausgeführt werden kann.
Mit freundlicher Genehmigung von javarevisited.blogspot.com
Dies waren einige der bemerkenswerten Unterschiede zwischen Thread und Runnable in Java. Wenn Sie weitere Unterschiede zwischen Thread und Runnable kennen, teilen Sie diese bitte über Kommentare mit. Ich persönlich verwende Runnable over Thread für dieses Szenario und empfehle, je nach Ihren Anforderungen die Runnable- oder Callable-Schnittstelle zu verwenden.
Der signifikante Unterschied ist jedoch.
Wenn Sie
extends Thread
klassifizieren, erstellt jeder Ihrer Threads ein eindeutiges Objekt und ordnet es zu. Wenn Sieimplements Runnable
dasselbe Objekt für mehrere Threads freigeben.quelle
Eigentlich ist es nicht klug , zu vergleichen
Runnable
undThread
miteinander.Diese beiden haben eine Abhängigkeit und Beziehung in Multithreading genau wie
Wheel and Engine
Beziehung des Kraftfahrzeugs.Ich würde sagen, es gibt nur einen Weg für Multithreading mit zwei Schritten. Lassen Sie mich meinen Standpunkt klarstellen.
Runnable:
Bei der Implementierung
interface Runnable
bedeutet dies, dass Sie etwas erstellen, das sichrun able
in einem anderen Thread befindet. Wenn Sie jetzt etwas erstellen, das in einem Thread ausgeführt werden kann (das in einem Thread ausgeführt werden kann), müssen Sie keinen Thread erstellen.Die Klasse
MyRunnable
ist also nichts anderes als eine gewöhnliche Klasse mit einervoid run
Methode. Und seine Objekte sind einige gewöhnliche Objekte mit nur einer Methode,run
die beim Aufruf normal ausgeführt wird. (es sei denn, wir übergeben das Objekt in einem Thread).Thread:
class Thread
Ich würde sagen, eine ganz besondere Klasse mit der Fähigkeit, einen neuen Thread zu starten, der tatsächlich Multithreading durch seinestart()
Methode ermöglicht.Warum nicht weise vergleichen?
Weil wir beide für Multithreading brauchen.
Für Multithreading benötigen wir zwei Dinge:
Technisch und theoretisch sind beide notwendig, um einen Thread zu starten. Einer wird ausgeführt und einer wird ausgeführt (wie
Wheel and Engine
bei einem Kraftfahrzeug).Aus diesem Grund können Sie einen Thread nicht starten,
MyRunnable
wenn Sie ihn an eine Instanz von übergeben müssenThread
.Es ist jedoch möglich, einen Thread nur mit zu erstellen und auszuführen,
class Thread
da ClassThread
implementiert,Runnable
sodass wir alle wissen, dass es sichThread
auch um einRunnable
Inside handelt.Schließlich
Thread
undRunnable
ergänzen sich für Multithreading nicht Konkurrenten oder Ersatz.quelle
ThreadA
keinen Sinn mehr hatSie sollten Runnable implementieren, aber wenn Sie unter Java 5 oder höher arbeiten, sollten Sie es nicht mit starten,
new Thread
sondern stattdessen einen ExecutorService verwenden. Weitere Informationen finden Sie unter: So implementieren Sie einfaches Threading in Java .quelle
Ich bin kein Experte, aber ich kann mir einen Grund vorstellen, Runnable zu implementieren, anstatt Thread zu erweitern: Java unterstützt nur die Einzelvererbung, sodass Sie nur eine Klasse erweitern können.
Bearbeiten: Dies sagte ursprünglich "Die Implementierung einer Schnittstelle erfordert weniger Ressourcen." Sie müssen jedoch in beiden Fällen eine neue Thread-Instanz erstellen, sodass dies falsch war.
quelle
Ich würde sagen, es gibt einen dritten Weg:
Vielleicht wird dies ein wenig durch meine jüngste starke Verwendung von Javascript und Actionscript 3 beeinflusst, aber auf diese Weise muss Ihre Klasse keine ziemlich vage Oberfläche wie implementieren
Runnable
.quelle
Mit der Veröffentlichung von Java 8 gibt es jetzt eine dritte Option.
Runnable
ist eine funktionale Schnittstelle , dh Instanzen davon können mit Lambda-Ausdrücken oder Methodenreferenzen erstellt werden.Ihr Beispiel kann ersetzt werden durch:
oder wenn Sie eine
ExecutorService
und eine Methodenreferenz verwenden möchten :Diese sind nicht nur viel kürzer als Ihre Beispiele, sondern bieten auch viele der Vorteile, die in anderen Antworten zur Verwendung von
Runnable
Over angegeben sindThread
, z. B. Einzelverantwortung und Verwendung von Komposition, da Sie sich nicht auf das Verhalten des Threads spezialisieren. Auf diese Weise wird auch vermieden, dass eine zusätzliche Klasse erstellt wird, wenn Sie nur eine benötigen,Runnable
wie Sie es in Ihren Beispielen tun.quelle
() -> {}
die benutzerdefinierte Logik darstellen soll, die jemand benötigt? Also wäre es besser gesagt als() -> { /* Code here */ }
?Das Instanziieren einer Schnittstelle führt zu einer saubereren Trennung zwischen Ihrem Code und der Implementierung von Threads. Daher würde ich in diesem Fall lieber Runnable implementieren.
quelle
Jeder hier scheint zu denken, dass die Implementierung von Runnable der richtige Weg ist, und ich bin nicht wirklich anderer Meinung, aber meiner Meinung nach gibt es auch einen Grund, Thread zu erweitern. Tatsächlich haben Sie dies in Ihrem Code demonstriert.
Wenn Sie Runnable implementieren, hat die Klasse, die Runnable implementiert, keine Kontrolle über den Threadnamen. Es ist der aufrufende Code, der den Threadnamen wie folgt festlegen kann:
Wenn Sie jedoch Thread erweitern, können Sie dies innerhalb der Klasse selbst verwalten (genau wie in Ihrem Beispiel nennen Sie den Thread 'ThreadB'). In diesem Fall haben Sie:
A) könnte ihm einen nützlicheren Namen für Debugging-Zwecke geben
B) erzwingen, dass dieser Name für alle Instanzen dieser Klasse verwendet wird (es sei denn, Sie ignorieren die Tatsache, dass es sich um einen Thread handelt, und führen die obigen Schritte so aus, als ob es sich um eine ausführbare Datei handelt, aber wir sprechen hier auf jeden Fall über Konventionen ignoriere diese Möglichkeit, die ich fühle).
Sie können zum Beispiel sogar einen Stack-Trace seiner Erstellung nehmen und diesen als Thread-Namen verwenden. Dies mag seltsam erscheinen, kann jedoch je nach Struktur Ihres Codes für Debugging-Zwecke sehr nützlich sein.
Dies mag wie eine kleine Sache erscheinen, aber wenn Sie eine sehr komplexe Anwendung mit vielen Threads haben und plötzlich die Dinge "gestoppt" haben (entweder aus Gründen des Deadlocks oder möglicherweise aufgrund eines Fehlers in einem Netzwerkprotokoll, der geringer wäre Offensichtlich - oder aus anderen endlosen Gründen) ist es nicht immer sehr nützlich, einen Stack-Dump von Java zu erhalten, bei dem alle Threads als "Thread-1", "Thread-2" und "Thread-3" bezeichnet werden (dies hängt davon ab, wie Ihre Threads sind strukturiert und ob Sie anhand ihrer Stapelverfolgung sinnvoll erkennen können, welche welche ist - nicht immer möglich, wenn Sie Gruppen mit mehreren Threads verwenden, die alle denselben Code ausführen).
Allerdings können Sie dies natürlich auch generisch tun, indem Sie eine Erweiterung der Thread-Klasse erstellen, die ihren Namen auf einen Stack-Trace ihres Erstellungsaufrufs setzt, und diesen dann mit Ihren Runnable-Implementierungen anstelle der Standard-Java-Thread-Klasse verwenden (siehe unten) Zusätzlich zum Stack-Trace enthält der Thread-Name möglicherweise weitere kontextspezifische Informationen, die für das Debuggen nützlich sind (ein Verweis auf eine der vielen Warteschlangen oder Sockets, die beispielsweise verarbeitet werden könnten. In diesem Fall möchten Sie dies möglicherweise bevorzugen Erweitern Sie Thread speziell für diesen Fall, damit der Compiler Sie (oder andere Benutzer Ihrer Bibliotheken) zwingen kann, bestimmte Informationen (z. B. die betreffende Warteschlange / den betreffenden Socket) zur Verwendung im Namen zu übergeben.
Hier ist ein Beispiel für den generischen Thread mit dem aufrufenden Stack-Trace als Namen:
und hier ist ein Beispiel der Ausgabe, in der die beiden Namen verglichen werden:
quelle
Runnable
kann tatsächlich den Threadnamen steuern, da der Thread, in dem der Code ausgeführt wird, per Definition der aktuelle Thread ist (und jeder Code, der die Sicherheitsüberprüfungen besteht, die Kontrolle über die Threadnamen hat). Wenn man bedenkt, dass man die Hälfte seines Beitrags "omg, was ist mit Thread-Namen!" Widmet, scheint das eine ziemlich große Sache zu sein.Ausführbar, weil:
Auch wenn Sie jetzt nichts davon brauchen, können Sie es in Zukunft tun. Da das Überschreiben von Thread keinen Vorteil hat, ist Runnable eine bessere Lösung.
quelle
Da dies ein sehr beliebtes Thema ist und die guten Antworten überall verteilt und ausführlich behandelt werden, hielt ich es für gerechtfertigt, die guten Antworten der anderen in einer präziseren Form zusammenzufassen, damit Neulinge im Voraus einen einfachen Überblick haben:
Normalerweise erweitern Sie eine Klasse, um Funktionen hinzuzufügen oder zu ändern. Also, wenn Sie nicht wollen , zu überschreiben jedes Thema Verhalten , dann Runnable verwenden.
Im gleichen Licht, wenn Sie nicht brauchen zu vererben Thread Methoden können Sie , ohne dass Sie Kopf Runnable unter Verwendung.
Einzelvererbung : Wenn Sie Thread erweitern, können Sie keine Erweiterung von einer anderen Klasse vornehmen. Wenn Sie dies also tun müssen, müssen Sie Runnable verwenden.
Es ist ein gutes Design, Domänenlogik von technischen Mitteln zu trennen. In diesem Sinne ist es besser, eine ausführbare Aufgabe zu haben, die Ihre Aufgabe von Ihrem Läufer isoliert .
Sie können dasselbe ausführbare Objekt mehrmals ausführen. Ein Thread-Objekt kann jedoch nur einmal gestartet werden. (Vielleicht der Grund, warum Executoren Runnables akzeptieren, aber keine Threads.)
Wenn Sie Ihre Aufgabe als ausführbar entwickeln, haben Sie alle Flexibilität, wie Sie sie jetzt und in Zukunft verwenden können . Sie können es gleichzeitig über Executors, aber auch über Thread ausführen lassen. Und Sie können es auch weiterhin nicht gleichzeitig innerhalb desselben Threads verwenden / aufrufen, genau wie bei jedem anderen normalen Typ / Objekt.
Dies erleichtert auch die Trennung von Aufgabenlogik- und Parallelitätsaspekten in Ihren Komponententests .
Wenn Sie an dieser Frage interessiert sind, könnte Sie auch der Unterschied zwischen Callable und Runnable interessieren .
quelle
Der Unterschied zwischen dem Erweitern des Threads und dem Implementieren von Runnable ist:
quelle
Dies wird im Tutorial zum Definieren und Starten eines Threads von Oracle erläutert :
Mit anderen Worten, die Implementierung
Runnable
funktioniert in Szenarien, in denen Ihre Klasse eine andere Klasse als erweitertThread
. Java unterstützt keine Mehrfachvererbung. Außerdem ist eine ErweiterungThread
nicht möglich, wenn einige der übergeordneten Thread-Verwaltungs-APIs verwendet werden. Das einzige Szenario, in dem eine ErweiterungThread
vorzuziehen ist, ist eine kleine Anwendung, die in Zukunft nicht mehr aktualisiert werden muss. Es ist fast immer besser zu implementieren,Runnable
da es flexibler ist, wenn Ihr Projekt wächst. Eine Designänderung hat keine großen Auswirkungen, da Sie viele Schnittstellen in Java implementieren können, aber nur eine Klasse erweitern können.quelle
Die einfachste Erklärung wäre, indem
Runnable
wir implementieren, dass wir dasselbe Objekt mehreren Threads und jedem zuweisen könnenThread
denselben Objektstatus und dasselbe Verhalten aufweist.Angenommen, es gibt zwei Threads, Thread1 fügt eine Ganzzahl in ein Array ein und Thread2 nimmt Ganzzahlen aus dem Array, wenn das Array gefüllt ist. Beachten Sie, dass Thread2 den Status des Arrays kennen muss, unabhängig davon, ob Thread1 funktioniert ihn gefüllt hat oder nicht.
Durch die Implementierung
Runnable
können Sie diese Flexibilität nutzen, um das Objekt gemeinsam zu nutzen, währendextends Thread
Sie für jeden Thread neue Objekte erstellen können. Daher geht jede Aktualisierung durch Thread1 an Thread2 verloren.quelle
Wenn ich mich nicht irre, ist es mehr oder weniger ähnlich
Was ist der Unterschied zwischen einer Schnittstelle und einer abstrakten Klasse?
erweitert stellt " Is A " Beziehung & Schnittstelle bietet " Hat a Fähigkeit ".
Bevorzugen Sie implementiert Runnable :
Bevorzugen " erweitert Thread ":
Im Allgemeinen müssen Sie das Thread-Verhalten nicht überschreiben. Damit implementiert Runnable für die meisten bevorzugt.
In einem anderen Sinne mit Advanced
ExecutorService
oderThreadPoolExecutorService
API mehr Flexibilität und Kontrolle.Schauen Sie sich diese SE-Frage an:
ExecutorService gegen Casual Thread Spawner
quelle
Durch das Trennen der Thread-Klasse von der Runnable-Implementierung werden auch potenzielle Synchronisierungsprobleme zwischen dem Thread und der run () -Methode vermieden. Eine separate ausführbare Datei bietet im Allgemeinen mehr Flexibilität bei der Referenzierung und Ausführung von ausführbarem Code.
quelle
Das ist das S von SOLID : Einzelverantwortung.
Ein Thread verkörpert den laufenden Kontext (wie im Ausführungskontext: Stapelrahmen, Thread-ID usw.) der asynchronen Ausführung eines Codeteils. Dieser Code sollte idealerweise dieselbe Implementierung sein, ob synchron oder asynchron .
Wenn Sie sie in einer Implementierung bündeln, geben Sie dem resultierenden Objekt zwei nicht verwandte Ursachen für Änderungen:
Wenn die von Ihnen verwendete Sprache Teilklassen oder Mehrfachvererbung unterstützt, können Sie jede Ursache in einer eigenen Superklasse trennen. Sie läuft jedoch auf das Gleiche hinaus wie das Erstellen der beiden Objekte, da sich ihre Funktionssätze nicht überschneiden. Das ist für die Theorie.
In der Praxis muss ein Programm im Allgemeinen nicht komplexer als nötig sein. Wenn ein Thread an einer bestimmten Aufgabe arbeitet, ohne diese Aufgabe jemals zu ändern, macht es wahrscheinlich keinen Sinn, die Aufgaben zu getrennten Klassen zu machen, und Ihr Code bleibt einfacher.
Im Kontext von Java ist es wahrscheinlich einfacher, direkt mit eigenständigen Klassen zu beginnen und ihre Instanzen an (oder ) Instanzen zu übergeben , da die Funktion bereits vorhanden ist . Sobald Sie sich an dieses Muster gewöhnt haben, ist es nicht schwieriger zu verwenden (oder sogar zu lesen) als der einfache Fall eines ausführbaren Threads.
Runnable
Thread
Executor
quelle
Ein Grund, warum Sie eine Schnittstelle implementieren möchten, anstatt eine Basisklasse zu erweitern, besteht darin, dass Sie bereits eine andere Klasse erweitern. Sie können nur eine Klasse erweitern, aber Sie können eine beliebige Anzahl von Schnittstellen implementieren.
Wenn Sie Thread erweitern, verhindern Sie grundsätzlich, dass Ihre Logik von einem anderen Thread als "this" ausgeführt wird. Wenn Sie nur möchten, dass ein Thread Ihre Logik ausführt, ist es besser, Runnable zu implementieren.
quelle
Wenn Sie runnable verwenden, können Sie den Speicherplatz für jede andere Klasse speichern.
quelle
Können wir den Grund, warum wir wollten, dass sich unsere Klasse als Klasse verhält, noch einmal besuchen?
Thread
? Es gibt überhaupt keinen Grund, wir wollten nur eine Aufgabe ausführen, höchstwahrscheinlich in einem asynchronen Modus, was genau bedeutet, dass die Ausführung der Aufgabe von unserem Hauptthread und dem Hauptthread verzweigen muss, wenn sie vorzeitig beendet wird, möglicherweise wartet oder nicht für den verzweigten Pfad (Aufgabe).Wenn dies der ganze Zweck ist, wo sehe ich dann die Notwendigkeit eines speziellen Threads? Dies kann erreicht werden, indem ein RAW-Thread aus dem Thread-Pool des Systems ausgewählt und ihm unsere Aufgabe zugewiesen wird (möglicherweise eine Instanz unserer Klasse), und das ist es.
Befolgen wir also das OOP-Konzept und schreiben Sie eine Klasse des Typs, den wir benötigen. Es gibt viele Möglichkeiten, Dinge richtig zu machen.
Wir brauchen eine Aufgabe, also schreiben Sie eine Aufgabendefinition, die auf einem Thread ausgeführt werden kann. Verwenden Sie also Runnable.
Denken Sie immer daran, dass dies
implements
speziell zum Vermitteln eines Verhaltens undextends
zum Vermitteln eines Features / einer Eigenschaft verwendet wird.Wir möchten nicht, dass die Eigenschaft des Threads, sondern dass sich unsere Klasse als eine Aufgabe verhält, die ausgeführt werden kann.
quelle
Ja, wenn Sie den ThreadA-Aufruf aufrufen, müssen Sie die Startmethode nicht aufrufen, und die Ausführungsmethode wird nur nach dem Aufruf der ThreadA-Klasse aufgerufen. Wenn Sie jedoch den ThreadB-Aufruf verwenden, muss der Startthread für die Aufrufausführungsmethode erforderlich sein. Wenn Sie weitere Hilfe haben, antworten Sie mir.
quelle
Ich finde es aus all den genannten Gründen am nützlichsten, Runnable zu verwenden, aber manchmal möchte ich Thread erweitern, damit ich meine eigene Thread-Stoppmethode erstellen und sie direkt in dem von mir erstellten Thread aufrufen kann.
quelle
Java unterstützt keine Mehrfachvererbung. Wenn Sie also die Thread-Klasse erweitern, wird keine andere Klasse erweitert.
Beispiel: Wenn Sie ein Applet erstellen, muss es die Applet-Klasse erweitern. Hier besteht die einzige Möglichkeit zum Erstellen eines Threads in der Implementierung der Runnable-Schnittstelle
quelle
Runnable
ist eine Schnittstelle, währendThread
es sich um eine Klasse handelt, die diese Schnittstelle implementiert. Aus gestalterischer Sicht sollte es eine klare Trennung zwischen der Definition einer Aufgabe und ihrer Ausführung geben. Ersteres liegt in der Verantwortung einerRunnalbe
Implementierung, und letzteres ist Aufgabe derThread
Klasse. In den meisten FällenRunnable
ist die Implementierung der richtige Weg.quelle
Unterschied zwischen Thread und ausführbar. Wenn wir einen Thread mit der Thread-Klasse erstellen, entspricht die Anzahl der Threads der Anzahl der von uns erstellten Objekte. Wenn wir einen Thread erstellen, indem wir die ausführbare Schnittstelle implementieren, können wir ein einzelnes Objekt zum Erstellen mehrerer Threads verwenden. Ein einzelnes Objekt wird also von mehreren Threads gemeinsam genutzt. Daher wird weniger Speicher benötigt
Also je nach Anforderung, wenn unsere Daten nicht sensibel sind. Damit es von mehreren Threads gemeinsam genutzt werden kann, können wir die Runnable-Schnittstelle verwenden.
quelle
Meine zwei Cent hier hinzufügen - Immer wann immer möglich verwenden
implements Runnable
. Im Folgenden finden Sie zwei Einschränkungen, warum Sieextends Thread
s nicht verwenden solltenIdealerweise sollten Sie die Thread-Klasse niemals erweitern. Die
Thread
Klasse sollte gemacht werdenfinal
. Zumindest seine Methoden mögenthread.getId()
. In dieser Diskussion finden Sie einen Fehler im Zusammenhang mit der Erweiterung vonThread
s.Diejenigen, die gerne Rätsel lösen, können einen weiteren Nebeneffekt beim Erweitern von Thread sehen. Der folgende Code gibt nicht erreichbaren Code aus, wenn niemand sie benachrichtigt.
Weitere Informationen finden Sie unter http://pastebin.com/BjKNNs2G .
quelle