it-swarm.com.de

"implementiert Runnable" vs "erweitert Thread" in Java

Nach der Zeit, die ich mit Threads in Java verbracht habe, 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 signifikante Unterschiede zwischen diesen beiden Codeblöcken?

1971
user65374

Ja: implementiert Runnable ist die bevorzugte Methode, IMO. Sie sind nicht wirklich auf das Verhalten des Threads spezialisiert. Sie geben ihm nur etwas zum Laufen. Das heißt Komposition ist der philosophisch "reinere" Weg.

In praktischen Ausdrücken bedeutet dies, dass Sie Runnable implementieren und auch von einer anderen Klasse aus erweitern können.

1580
Jon Skeet

tl; dr: implementiert Runnable ist besser. Die Einschränkung ist jedoch wichtig

Im Allgemeinen würde ich empfehlen, etwas wie Runnable anstelle von Thread zu verwenden, da Sie so Ihre Arbeit nur lose mit Ihrer Wahl der Parallelität verknüpfen können. Wenn Sie beispielsweise ein Runnable verwenden und später feststellen, dass dies kein eigenes Thread erfordert, können Sie einfach threadA.run () aufrufen.

Einschränkung: Ich rate hier nachdrücklich von der Verwendung von Rohfäden ab. Ich bevorzuge die Verwendung von Callables und FutureTasks (Aus dem Javadoc: "Eine kündbare asynchrone Berechnung"). Die Integration von Timeouts, das ordnungsgemäße Abbrechen und das Thread-Pooling der modernen Parallelunterstützung sind für mich alle viel nützlicher als Stapel roher Threads.

Follow-up: Es gibt einen FutureTask -Konstruktor , mit dem Sie Runnables verwenden können (wenn Sie es sind) am bequemsten mit) und trotzdem den Nutzen der modernen Nebenläufigkeitstools. m das Javadoc zu zitieren :

Wenn Sie kein bestimmtes Ergebnis benötigen, ziehen Sie die Verwendung von Konstruktionen des Formulars in Betracht:

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

Wenn wir also ihr runnable durch Ihr threadA ersetzen, erhalten wir Folgendes:

new FutureTask<Object>(threadA, null)

Eine weitere Option, mit der Sie näher an Runnables bleiben können, ist ThreadPoolExecutor . Mit der execute -Methode können Sie ein Runnable übergeben, um "die angegebene Aufgabe irgendwann in der Zukunft auszuführen".

Wenn Sie versuchen möchten, einen Thread-Pool zu verwenden, wird das obige Codefragment folgendermaßen aussehen (mithilfe der Executors.newCachedThreadPool () factory-Methode):

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

Moral der Geschichte:

Nur erben, wenn Sie ein bestimmtes Verhalten überschreiben möchten.

Oder besser gesagt:

Weniger erben, Schnittstelle mehr.

249
panzerschreck

Nun, so viele gute Antworten, ich möchte mehr dazu hinzufügen. Dies wird helfen, Extending v/s Implementing Thread zu verstehen.
Extends bindet zwei Klassendateien sehr eng und kann dazu führen, dass einige ziemlich schwer mit Code umzugehen sind.

Beide Ansätze machen den gleichen Job, aber es gab einige Unterschiede.
Der häufigste Unterschied ist

  1. Wenn Sie die Thread-Klasse erweitern, können Sie danach keine andere Klasse mehr erweitern, die Sie benötigt haben. (Wie Sie wissen, erlaubt Java nicht, mehr als eine Klasse zu erben).
  2. Wenn Sie Runnable implementieren, können Sie Platz für Ihre Klasse sparen, um künftig oder jetzt jede andere Klasse zu erweitern.

Ein signifikanter Unterschied zwischen dem Implementieren von Runnable und dem Erweitern 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 soll Ihnen das Verständnis erleichtern

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

Ausgabe des obigen Programms.

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

Beim Runnable-Interface-Ansatz wird nur eine Instanz einer Klasse erstellt, die von verschiedenen Threads gemeinsam genutzt wurde. Daher wird der Wert des Zählers für jeden Threadzugriff erhöht.

Während, Thread-Klassen-Ansatz, müssen Sie für jeden Thread-Zugriff eine separate Instanz erstellen. Daher wird für jede Klasseninstanz ein anderer Speicher zugewiesen und jeder hat einen eigenen Zähler. Der Wert bleibt gleich, was bedeutet, dass kein Inkrement auftritt, da keine der Objektreferenzen identisch ist.

Wann benutzt man Runnable?
Verwenden Sie die ausführbare Schnittstelle, wenn Sie auf dieselben Ressourcen aus der Gruppe von Threads zugreifen möchten. Vermeiden Sie hier die Verwendung der Thread-Klasse, da die Erstellung mehrerer Objekte mehr Speicherplatz beansprucht und einen hohen Leistungsaufwand bedeutet.

Eine Klasse, die Runnable implementiert, ist kein Thread, sondern nur eine Klasse. Damit ein Runnable zu einem Thread wird, müssen Sie eine Instanz von Thread 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 Superklasse erweitert werden muss, ist die Implementierung der Runnable-Schnittstelle geeigneter 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!

212
Rupesh Yadav

Was mich überrascht hat, ist, dass die Implementierung von 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 Runnable implementieren, muss dies nicht der Fall sein. Sie können es in einem Thread ausführen oder an einen Executor-Service weitergeben oder es einfach als Task in einer einzelnen Thread-Anwendung weitergeben (möglicherweise zu einem späteren Zeitpunkt, jedoch innerhalb desselben Threads). Die Optionen sind viel offener, wenn Sie nur Runnable verwenden, als wenn Sie sich an Thread binden.

77
Herms

Wenn Sie eine andere Klasse implementieren oder erweitern möchten, ist die Schnittstelle Runnable am besten geeignet, andernfalls ist die Klasse Thread vorzuziehen, wenn keine andere Klasse erweitert oder implementiert werden soll.

Der häufigste Unterschied ist

enter image description here

Wenn Sie extends Thread klassifizieren, 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, können Sie Platz für Ihre Klasse sparen, um jede andere Klasse in Zukunft oder jetzt zu erweitern.

  • Java unterstützt keine Mehrfachvererbungen. Dies bedeutet, dass Sie nur eine Klasse in Java erweitern können. Sobald Sie die Thread-Klasse erweitert haben, haben Sie Ihre Chance verloren, eine andere Klasse in Java zu erweitern oder zu 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 am Thread keine Änderungen vornehmen, verwenden Sie stattdessen die ausführbare Schnittstelle.

  • Die ausführbare Oberfläche stellt eine Aufgabe dar, die entweder von einem normalen Thread oder von Executors oder auf andere Weise ausgeführt werden kann. Die logische Trennung von Task als ausführbar als Thread ist daher eine gute Entwurfsentscheidung.

  • Das Trennen der Aufgabe als ausführbar bedeutet, dass wir die Aufgabe wiederverwenden können und auch die Freiheit haben, sie auf verschiedenen Wegen auszuführen. da du einen Thread nicht neu starten kannst, sobald er fertig ist. wieder Runnable vs Thread für die Aufgabe, Runnable ist der Gewinner.

  • Java-Designer erkennen dies und deshalb akzeptieren Executoren Runnable als Task und haben 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 zu Thread vs Runnable kennen, teilen Sie diese bitte über Kommentare mit. Ich persönlich verwende Runnable over Thread für dieses Szenario und empfehle die Verwendung einer Runnable- oder Callable-Schnittstelle, basierend auf Ihren Anforderungen.

Der wesentliche Unterschied ist jedoch.

Wenn Sie extends Thread klassifizieren, erstellt jeder Ihrer Threads ein eindeutiges Objekt und ordnet es ihm zu. Wenn Sie implements Runnable, wird dasselbe Objekt für mehrere Threads freigegeben.

73

Tatsächlich ist es nicht ratsam, Runnable und Thread miteinander zu vergleichen.

Diese beiden haben eine Abhängigkeit und Beziehung im Multithreading, genau wie die Beziehung _Wheel and Engine_ des Kraftfahrzeugs.

Ich würde sagen, es gibt nur einen Weg zum Multithreading mit zwei Schritten. Lassen Sie mich das klarstellen.

lauffähig:
Wenn Sie _interface Runnable_ implementieren, bedeutet dies, dass Sie etwas erstellen, das _run able_ in einem anderen Thread ist. Wenn Sie jetzt etwas erstellen, das in einem Thread ausgeführt werden kann (das in einem Thread ausgeführt werden kann), bedeutet dies nicht, dass Sie einen Thread erstellen.
Die Klasse MyRunnable ist also nichts anderes als eine gewöhnliche Klasse mit der Methode _void run_. Und es handelt sich bei den Objekten um 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_, würde ich sagen Eine ganz besondere Klasse mit der Fähigkeit, einen neuen Thread zu starten, der durch seine Methode start() tatsächlich Multithreading ermöglicht.

Warum nicht vergleichen?
Weil wir beide für das Multithreading brauchen.

Für das Multithreading benötigen wir zwei Dinge:

  • Etwas, das in einem Thread ausgeführt werden kann (ausführbar).
  • Etwas, das einen neuen Thread (Thread) starten kann.

Technisch und theoretisch sind beide erforderlich, um einen Thread zu starten. Einer wird run und einer wird make it run (Like Wheel and Engine = des Kraftfahrzeugs).

Aus diesem Grund können Sie einen Thread nicht mit MyRunnable starten, sondern müssen ihn an eine Instanz von Thread übergeben.

Aber Es ist möglich, einen Thread nur mit _class Thread_ zu erstellen und auszuführen, da die Klasse ThreadRunnable implementiert, sodass wir alle wissen, dass Thread auch ein Runnable ist.

Schließlich ergänzen sich Thread und Runnable für Multithreading, nicht für Konkurrenz oder Ersatz.

68
Saif

Sie sollten Runnable implementieren, aber wenn Sie mit Java 5 oder höher arbeiten, sollten Sie es nicht mit new Thread starten, sondern stattdessen ein ExecutorService verwenden. Für Details siehe: Wie man einfaches Threading in Java implementiert .

44
Fabian Steeg

Ich bin kein Experte, aber ich kann mir einen Grund vorstellen, Runnable zu implementieren, anstatt Thread zu erweitern: Java unterstützt nur eine einzelne Vererbung, sodass Sie nur eine Klasse erweitern können.

Bearbeiten: Ursprünglich lautete dies "Das Implementieren einer Schnittstelle erfordert weniger Ressourcen." Sie müssen jedoch in beiden Fällen eine neue Thread-Instanz erstellen, sodass dies falsch war.

33
Powerlord

Ich würde sagen, es gibt einen dritten Weg:

public class Something {

    public void justAnotherMethod() { ... }

}

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

Möglicherweise wird dies ein wenig von meiner starken Nutzung von Javascript und Actionscript 3 beeinflusst, aber auf diese Weise muss Ihre Klasse keine ziemlich vage Schnittstelle wie Runnable implementieren.

20

Mit der Veröffentlichung von Java 8 gibt es jetzt eine dritte Option.

Runnable ist eine funktionale Schnittstelle , was bedeutet, dass Instanzen davon mit Lambda-Ausdrücken oder Methodenreferenzen erstellt werden können.

Ihr Beispiel kann ersetzt werden durch:

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

oder wenn Sie einen ExecutorService und einen Methodenverweis verwenden möchten:

executor.execute(runner::run)

Diese sind nicht nur viel kürzer als Ihre Beispiele, sondern bieten auch viele der Vorteile, die in anderen Antworten zur Verwendung von Runnable gegenüber Thread angegeben sind, z Verhalten des Threads. Auf diese Weise wird auch vermieden, dass eine zusätzliche Klasse erstellt wird, wenn Sie nur ein Runnable benötigen, wie Sie es in Ihren Beispielen tun.

18
Alex

Durch das Instanziieren einer Schnittstelle wird eine klarere Trennung zwischen Ihrem Code und der Implementierung von Threads erzielt. Daher würde ich in diesem Fall die Implementierung von Runnable vorziehen.

16
starblue

Jeder hier scheint der Meinung zu sein, dass die Implementierung von Runnable der richtige Weg ist, und ich bin damit nicht einverstanden, aber meiner Meinung nach gibt es auch einen Grund, Thread zu erweitern. Tatsächlich haben Sie dies in Ihrem Code sozusagen demonstriert.

Wenn Sie Runnable implementieren, hat die Klasse, die Runnable implementiert, keine Kontrolle über den Thread-Namen. Der aufrufende Code kann den Thread-Namen folgendermaßen festlegen:

new Thread(myRunnable,"WhateverNameiFeelLike");

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) Geben Sie ihm möglicherweise einen nützlicheren Namen für Debugging-Zwecke

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 einen ausführbaren Thread handelt ignoriere diese Möglichkeit, die ich fühle).

Sie können beispielsweise sogar einen Stack-Trace seiner Erstellung erstellen und diesen als Thread-Namen verwenden. Dies mag seltsam erscheinen, aber je nachdem, wie Ihr Code aufgebaut ist, kann es zu Debugging-Zwecken 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 Dinge "stehen bleiben" (entweder aus Gründen eines 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 Stapelspeicherauszug von Java zu erhalten, wo alle Threads als 'Thread-1', 'Thread-2', 'Thread-3' bezeichnet werden hängt davon ab, wie Ihre Threads strukturiert sind und ob Sie anhand ihres Stack-Trace erkennen können, welcher Thread welcher ist. Dies ist nicht immer möglich, wenn Sie Gruppen von 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 des Standards Java Thread-Klasse (siehe unten), aber zusätzlich zum Stack-Trace gibt es möglicherweise kontextspezifischere Informationen, die für das Debuggen im Thread-Namen nützlich sind (ein Verweis auf eine von vielen Warteschlangen oder Sockets, in denen sie beispielsweise verarbeitet werden könnten In diesem Fall möchten Sie Thread möglicherweise speziell für diesen Fall erweitern, 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 weiterzuleiten.

Hier ist ein Beispiel für den generischen Thread mit dem aufrufenden Stack-Trace als Namen:

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

und hier ist ein Beispiel für die Ausgabe, in der die beiden Namen verglichen werden:

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]
12
AntonyM

Lauffähig weil:

  • Lässt der Runnable-Implementierung mehr Flexibilität, um eine weitere Klasse zu erweitern
  • Trennt den Code von der Ausführung
  • Ermöglicht es Ihnen, Ihre ausführbare Datei über einen Thread-Pool, den Ereignisthread oder auf andere Weise in der Zukunft auszuführen.

Auch wenn Sie dies jetzt noch nicht benötigen, können Sie dies in Zukunft tun. Da das Überschreiben von Thread keinen Vorteil hat, ist Runnable eine bessere Lösung.

11
n13

Da dies ein sehr beliebtes Thema ist und die guten Antworten weit verbreitet und ausführlich behandelt werden, erschien es mir gerechtfertigt, die guten Antworten der anderen in einer präziseren Form zusammenzufassen, damit Neulinge einen einfachen Überblick haben:

  1. Normalerweise erweitern Sie eine Klasse, um Funktionen hinzuzufügen oder zu ändern. Also, , wenn Sie nicht möchten, dass überschreibt any Thread-Verhalten , dann benutze Runnable.

  2. Im selben Licht, , wenn Sie keine bis erben Thread-Methoden benötigen, können Sie darauf verzichten ) + Overhead mit Runnable.

  3. Single inheritance : Wenn Sie Thread erweitern, können Sie von keiner anderen Klasse aus erweitern. Wenn Sie dies tun müssen, müssen Sie Runnable verwenden.

  4. Es ist sinnvoll, Domänenlogik von technischen Mitteln zu trennen. In diesem Sinne ist es besser, eine ausführbare Aufgabe zu haben. isolating your task from Ihr Läufer .

  5. Sie können execute dasselbe Runnable object multiple times , ein Thread-Objekt kann jedoch nur einmal gestartet werden. (Vielleicht der Grund, warum Executors Runnables akzeptieren, aber keine Threads.)

  6. 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 gleichzeitig im selben Thread verwenden/aufrufen, genau wie bei jedem anderen normalen Typ/Objekt.

  7. Dies macht es auch einfacher, Aufgabenlogik und Nebenläufigkeit zu trennen - Aspekte in Ihre - Komponententests .

  8. Wenn Sie an dieser Frage interessiert sind, könnte Sie auch das nterschied zwischen Callable und Runnable interessieren.

11
Jörg

Der Unterschied zwischen Extending Thread und Implementing Runnable ist:

enter image description here

9
Raman Gupta

Dies wird im Definieren und Starten eines Threads Tutorial von Oracle erläutert:

Welche dieser Redewendungen sollten Sie verwenden? Das erste Idiom, das ein Runnable-Objekt verwendet, ist allgemeiner, da das Runnable-Objekt eine andere Klasse als Thread unterklassifizieren kann. Das zweite Idiom ist in einfachen Anwendungen einfacher zu verwenden, wird jedoch durch die Tatsache eingeschränkt, dass Ihre Taskklasse ein Nachkomme von Thread sein muss. Diese Lektion konzentriert sich auf den ersten Ansatz, der die ausführbare Aufgabe vom Thread-Objekt trennt, das die Aufgabe ausführt. Dieser Ansatz ist nicht nur flexibler, sondern auch auf die später behandelten allgemeinen Thread-Management-APIs anwendbar.

Mit anderen Worten, die Implementierung von Runnable funktioniert in Szenarien, in denen Ihre Klasse eine andere Klasse als Thread erweitert. Java unterstützt keine Mehrfachvererbung. Außerdem ist das Erweitern von Thread nicht möglich, wenn einige der allgemeinen Thread-Management-APIs verwendet werden. Das einzige Szenario, in dem die Erweiterung von Thread vorzuziehen ist, ist eine kleine Anwendung, die in Zukunft nicht mehr aktualisiert werden muss. Es ist fast immer besser, Runnable zu implementieren, da es flexibler ist, wenn Ihr Projekt wächst. Eine Designänderung hat keine großen Auswirkungen, da Sie viele Schnittstellen in Java implementieren, sondern nur eine Klasse erweitern können.

8
Sionnach733

Wenn ich mich nicht irre, ist es mehr oder weniger ähnlich

Was ist der Unterschied zwischen einer Schnittstelle und einer abstrakten Klasse?

stellt fest, " ist eine " Beziehung & Schnittstelle bietet " hat eine " Fähigkeit.

Prefer implementiert Runnable :

  1. Wenn Sie die Thread-Klasse nicht erweitern und die Standardimplementierung der Thread-API nicht ändern müssen
  2. Wenn Sie ein Feuer ausführen und den Befehl vergessen
  3. Wenn Sie bereits eine andere Klasse erweitern

Bevorzugen Sie " erweitert Thread ":

  1. Wenn Sie eine dieser Thread - Methoden überschreiben müssen, wie auf der Oracle-Dokumentationsseite aufgeführt

Im Allgemeinen müssen Sie das Thread-Verhalten nicht überschreiben. Aus diesem Grund wird Runnable meistens bevorzugt.

Die Verwendung der erweiterten API ExecutorService oder ThreadPoolExecutorService bietet in einem anderen Fall mehr Flexibilität und Kontrolle.

Schauen Sie sich diese SE-Frage an:

ExecutorService vs Casual Thread Spawner

7
Ravindra babu

Die einfachste Erklärung wäre, dass wir durch die Implementierung von Runnable dasselbe Objekt mehreren Threads zuweisen können und jedes Thread dieselben Objektzustände 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. Damit thread2 funktioniert, muss bekannt sein, ob thread1 das Array gefüllt hat oder nicht.

Durch die Implementierung von Runnable haben Sie diese Flexibilität, um das Objekt gemeinsam zu nutzen, während Sie mit extends Thread neue Objekte für jeden Thread erstellen. Daher gehen alle Aktualisierungen von Thread1 an Thread2 verloren.

6
Shababb Karim

Das Trennen der Thread-Klasse von der Runnable-Implementierung vermeidet außerdem potenzielle Synchronisationsprobleme zwischen dem Thread und der run () -Methode. Ein separates Runnable bietet im Allgemeinen mehr Flexibilität bei der Referenzierung und Ausführung von ausführbarem Code.

5
Govula Srinivas

Das ist das S von SOLID : Einzelverantwortung.

Ein Thread verkörpert den laufenden Kontext (wie im Ausführungskontext: Stack-Frame, Thread-ID usw. ) der asynchronen Ausführung eines Codeteils. Dieses Stück Code sollte idealerweise dieselbe Implementierung sein, egal ob synchron oder asynchron .

Wenn Sie sie in einer Implementierung bündeln, geben Sie dem resultierenden Objekt zwei unabhängige Änderungsursachen an:

  1. thread-Behandlung in Ihrer Anwendung (dh Abfragen und Ändern des Ausführungskontexts)
  2. algorithmus, der vom Code-Teil (dem ausführbaren Teil) implementiert wird

Wenn die Sprache, die Sie verwenden, Teilklassen oder Mehrfachvererbung unterstützt, können Sie jede Ursache in einer eigenen übergeordneten Klasse trennen. Dies entspricht jedoch dem Zusammensetzen der beiden Objekte, da sich ihre Feature-Sets nicht überlappen. Das ist für die Theorie.

In der Praxis muss ein Programm im Allgemeinen nicht komplexer als nötig sein. Wenn Sie einen Thread für eine bestimmte Aufgabe haben, ohne diese Aufgabe jemals zu ändern, ist es wahrscheinlich sinnlos, die Aufgaben in separate Klassen zu unterteilen, und Ihr Code bleibt einfacher.

Im Kontext von Java ist es wahrscheinlich einfacher, direkt zu starten, da die Funktion bereits vorhanden ist Mit eigenständigen Klassen Runnable und deren Instanzen an Instanzen Thread (oder Executor) übergeben. Sobald dieses Muster verwendet hat , ist es nicht schwieriger zu verwenden (oder sogar zu lesen) als das einfache, ausführbare Thread-Gehäuse.

5
didierc

Ein Grund, warum Sie eine Schnittstelle implementieren möchten, anstatt eine Basisklasse zu erweitern, ist, 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 im Grunde genommen, dass Ihre Logik von einem anderen Thread als "this" ausgeführt wird. Wenn Sie nur möchten, dass some thread Ihre Logik ausführt, ist es besser, nur Runnable zu implementieren.

5
Nikhil A A

Können wir den Grund, warum wir wollten, dass unsere Klasse sich als Thread verhält, noch einmal besuchen? Es gibt überhaupt keinen Grund, wir wollten nur eine Aufgabe ausführen, höchstwahrscheinlich im asynchronen Modus, was genau bedeutet, dass die Ausführung der Aufgabe von unserem Hauptthread und dem Hauptthread abzweigen muss, wenn sie vorzeitig beendet wird, möglicherweise wartet oder nicht für den verzweigten Pfad (Task).

Wenn dies der ganze Zweck ist, wo sehe ich dann die Notwendigkeit eines spezialisierten Threads. Dies kann erreicht werden, indem ein RAW-Thread aus dem Thread-Pool des Systems abgerufen und ihm unsere Aufgabe zugewiesen wird (möglicherweise eine Instanz unserer Klasse) und das ist es.

Befolgen wir also das OOPs-Konzept und schreiben Sie eine Klasse der Art, die wir brauchen. 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. Also benutze Runnable.

Denken Sie immer daran, dass implements speziell verwendet wird, um ein Verhalten zu vermitteln, und extends wird verwendet, um ein Merkmal/eine Eigenschaft zu vermitteln.

Wir wollen nicht die Eigenschaft des Threads, sondern, dass sich unsere Klasse als Task verhält, die ausgeführt werden kann.

5
dharam

wenn Sie runable verwenden, können Sie den Platz für eine Erweiterung auf eine beliebige andere Klasse sparen.

5
user2771655

Ein Unterschied zwischen dem Implementieren von Runnable und dem Erweitern von Thread besteht darin, dass durch das Erweitern von Thread jedem Ihrer Threads ein eindeutiges Objekt zugeordnet wird, während beim Implementieren von Runnable viele Threads dieselbe Objektinstanz gemeinsam nutzen können.

Eine Klasse, die Runnable implementiert, ist kein Thread, sondern nur eine Klasse. Damit ein Runnable von einem Thread ausgeführt werden kann, müssen Sie eine Instanz von Thread erstellen und die Runnable-Instanz 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 Superklasse erweitert werden muss, ist die Implementierung der Runnable-Schnittstelle geeigneter 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. Wenn wir jedoch nur die Thread-Klasse erweitern, können wir keine andere Klasse erben.

4
Vishal

Ich finde es aus allen 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 auf dem von mir erstellten Thread aufrufen kann.

4

Der beste Weg für die meisten Worker-Threads besteht darin, das Threading vollständig in der Worker-Klasse zu kapseln, sodass nichts von außen stören und unerwünschte und ungültige Thread-/Klassenzustände verursachen kann.

Ich habe gerade ein Beispiel gepostet, daher teile ich dies auch mit Ihnen:

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

}
4
JayC667

Runnable ist eine Schnittstelle, während Thread eine Klasse ist, die diese Schnittstelle implementiert. Aus gestalterischer Sicht sollte eine klare Trennung zwischen der Definition und der Ausführung einer Aufgabe bestehen. Ersteres liegt in der Verantwortung einer Implementierung von Runnalbe, und letzteres ist Aufgabe der Klasse Thread. In den meisten Fällen ist die Implementierung von Runnable der richtige Weg.

4
developer110

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 von mehreren Threads gemeinsam genutzt. Daher wird weniger Speicher benötigt

Also je nach Anforderung, wenn unsere Daten nicht senstiv sind. So kann es zwischen mehreren Threads geteilt werden, wir können die Runnable-Schnittstelle verwenden.

4
Rohit Chugh

Meine zwei Cent hier hinzufügen - Immer wenn möglich implements Runnable verwenden. Im Folgenden sind zwei Vorsichtsmaßnahmen aufgeführt, warum Sie extends Threads nicht verwenden sollten.

  1. Idealerweise sollten Sie die Thread-Klasse niemals erweitern. Die Klasse Thread sollte final sein. Zumindest seine Methoden wie thread.getId(). Siehe this Diskussion für einen Fehler im Zusammenhang mit der Erweiterung von Threads.

  2. Wer gerne Rätsel löst, kann einen weiteren Nebeneffekt beim Erweitern von Thread feststellen. Der folgende Code gibt nicht erreichbaren Code aus, wenn sie von niemandem benachrichtigt werden.

Bitte siehe 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 ");
        }
    } 
}
4
veritas

Java unterstützt keine Mehrfachvererbung. Wenn Sie also die Thread-Klasse erweitern, wird keine andere Klasse erweitert.

Zum Beispiel: Wenn Sie ein Applet erstellen, muss es die Applet-Klasse erweitern, sodass hier der einzige Weg zum Erstellen eines Threads darin besteht, eine Runnable-Schnittstelle zu implementieren

4
Himanshu Mohta

Ja, wenn Sie ThreadA aufrufen, müssen Sie die start-Methode nicht aufrufen und run-Methode wird nur nach dem Aufruf der ThreadA-Klasse aufgerufen. Aber wenn Sie den ThreadB-Aufruf verwenden, müssen Sie den Start-Thread für die Aufruf-Ausführungsmethode benötigen. Wenn Sie weitere Hilfe haben, antworten Sie mir.

4
Manoj Kumar

Der Thread enthält ein Verhalten, auf das nicht zugegriffen werden soll.

  • die synchronisierte Sperre wird für Joins usw. verwendet.
  • es gibt Methoden, auf die Sie versehentlich zugreifen können.

wenn Sie jedoch die Unterklasse Thread berücksichtigen müssen, wird mehr Thread implementiert.

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

Wenn Sie dies ausführen, können Sie erwarten

id: 12345678, name: Hello World, state: testing

sie rufen jedoch nicht die Methoden auf, von denen Sie glauben, dass Sie sie sind, weil Sie die Methode in Thread not ThreadMain verwenden, und stattdessen sehen Sie so etwas wie

id: 11, name: Thread-0, state: RUNNABLE
3
Peter Lawrey

In dem seltenen Fall, dass Sie es nur einmal ausführen, sollten Sie Thread wegen DRY erweitern. Wenn Sie es mehrmals aufrufen, sollten Sie Runnable implementieren, da derselbe Thread nicht neu gestartet werden sollte.

2
Peter Rader

Durch die Erweiterung der Thread-Klasse kann die abgeleitete Klasse keine andere Basisklasse erweitern, da Java nur eine einzelne Vererbung zulässt. Im Gegenteil: Durch die Implementierung der ausführbaren Schnittstelle erweitert die Klasse noch die andere Basisklasse.

Der wichtigste Unterschied zwischen dem Implementieren von Runnable und dem Erweitern von Thread ist unten angegeben:

Indem Thread erweitert wird, ist die abgeleitete Klasse selbst ein Thread-Objekt, während das Implementing Runnable-Interface dasselbe Objekt für mehrere Threads verwendet.

2
rashedcs

Das ist vielleicht keine Antwort, aber trotzdem; Es gibt noch eine Möglichkeit, Threads zu erstellen:

Thread t = new Thread() {
    public void run() {
        // Code here
    }
}
2
AHugoH

1. Wenn Sie die Thread-Schnittstelle erweitern, wird Ihre Klasse so eingerichtet, dass sie sich nur als Thread verhält. Ihre neue Klasse wird wie ein erweiterter Thread.

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]

Es wird ein Thread erstellt, nicht das Testobjekt. Also wird es sich wie ein einziger Thread verhalten. Sie können die Instanz der Testklasse nicht für mehrere Threads freigeben.

2. Implementierung der ausführbaren Schnittstelle.

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 ==> [email protected]  --> this creates Test1 object.

Dieses Objekt kann von mehreren Threads gemeinsam genutzt werden.

jshell> Thread t1=new Thread(t1,"Hai");
t ==> Thread[Hai,5,main]

jshell> Thread t=new Thread(t1,"Hai");
t ==> Thread[Hai,5,main]

Ich denke, zu diesem Thema wurde schon viel diskutiert. obwohl dies in den Grundlagen hilfreich sein könnte.

1
Arasn

Ein einfacher Weg zu sagen ist: Wenn Sie eine Schnittstelle implementieren, das heißt, Sie implementieren alle Methoden davon und wenn Sie die Klasse erweitern, erben Sie eine Methode Ihrer Wahl ... In diesem Fall gibt es nur eine Methode mit dem Namen Run () so besser Runnable-Schnittstelle zu implementieren ..

1
samkit shah

Die Thread-Klasse definiert mehrere Methoden, die von der Erweiterungsklasse overriden sein können. Um jedoch einen Thread zu erstellen, müssen wir die run() -Methode überschreiben. Gleiches gilt auch für Runnable.

Runnable ist jedoch eine bevorzugte Methode zum Erstellen eines Threads. Die Hauptgründe sind:

  1. Da Runnable eine Schnittstelle ist, können Sie andere Klassen erweitern. Wenn Sie jedoch Thread erweitern, ist diese Option nicht mehr verfügbar.

  2. Wenn Sie nicht eine ganze Reihe von Thread Funktionen ändern oder erweitern und die Thread Klasse nicht erweitern, ist dies eine bevorzugte Methode.

1
Pritam Banerjee

Der Hauptunterschied zwischen Thread und Runnable ist: - Thread ist wie folgt: Worker (Runnable ausführen) - Runnable ist wie folgt: Job (von Thread auszuführen)

0
Pen Lymeng

Ich würde sagen, die eigentliche Aufgabe ist vom Thread entkoppelt. Wir können die Aufgabe im Falle von Runnable an Thread, Executor-Framework usw. weitergeben, während die Aufgabe mit dem Erweitern von Thread mit dem Thread-Objekt selbst gekoppelt ist. Die Aufgabenisolierung kann nicht durchgeführt werden, wenn der Thread erweitert wird. Es ist so, als würden wir die Aufgabe auf ein Thread-Objekt brennen, nur so etwas wie einen IC-Chip (und genauer gesagt, wir bekommen keinen Handgriff für die Aufgabe).

0
bharatj