2355 Stimmen

"implementiert Runnable" vs. "erweitert Thread" in Java

Nach der Zeit, die ich mit Threads in Java Ich habe diese beiden Möglichkeiten gefunden, Themen zu schreiben:

Mit implementiert Runnable :

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

Oder, mit erweitert 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?

66 Stimmen

Vielen Dank für diese Frage, die Antworten haben eine Menge Missverständnisse, die ich hatte, aufgeklärt. Ich schaute in den richtigen Weg, um Java-Threads zu tun, bevor SO existierte und es gab eine Menge von Fehlinformationen / veraltete Informationen da draußen.

5 Stimmen

Gibt es einen Grund, warum Sie den Thread ( aber ich empfehle es nicht ), können Sie präventiv mit interrupt() . Auch hier handelt es sich um eine Idee, die im richtigen Fall nützlich sein könnte, aber ich empfehle sie nicht.

0 Stimmen

Bitte lesen Sie auch die Antwort, die sehr gut erklärt ist: stackoverflow.com/q/5562720/285594

1829voto

Jon Skeet Punkte 1325502

Ja: Arbeitsgeräte Runnable ist IMO die beste Methode. Sie spezialisieren das Verhalten des Threads nicht wirklich. Du gibst ihm nur etwas zum Laufen. Das bedeutet Zusammensetzung ist die philosophisch "reineren" Weg zu gehen.

praktisch bedeutet dies, dass Sie folgende Maßnahmen durchführen können Runnable und auch von einer anderen Klasse erweitern... und Sie können auch implementieren Runnable über einen Lambda-Ausdruck ab Java 8.

162 Stimmen

Genau, gut gesagt. Welches Verhalten versuchen wir in Thread zu überschreiben, indem wir es erweitern? Ich würde behaupten, dass die meisten Leute nicht versuchen, irgendein Verhalten zu überschreiben, sondern versuchen, das Verhalten von Thread zu nutzen.

94 Stimmen

Als Randbemerkung: Wenn Sie einen Thread instanziieren und seine start()-Methode nicht aufrufen, erzeugen Sie in Java < 5 ein Speicherleck (dies geschieht nicht mit Runnables): stackoverflow.com/questions/107823/

26 Stimmen

Ein kleiner Vorteil von Runnable ist, dass Sie, wenn Sie unter bestimmten Umständen kein Threading verwenden wollen und den Code einfach nur ausführen wollen, die Möglichkeit haben, einfach run() aufzurufen. z.B. (sehr umständlich) if (numberCores > 4) myExecutor.excute(myRunnable); else myRunnable.run()

625voto

Bob Cross Punkte 22071

tl;dr: implementiert Runnable ist besser. Allerdings ist der Vorbehalt wichtig .

Im Allgemeinen würde ich empfehlen, etwas zu verwenden wie Runnable statt Thread weil es Ihnen erlaubt, Ihre Arbeit nur lose mit der von Ihnen gewählten Gleichzeitigkeit zu verbinden. Wenn Sie zum Beispiel eine Runnable und später zu entscheiden, dass dies in der Tat nicht eine eigene Thread können Sie einfach threadA.run() aufrufen.

Vorbehalt: Hier in der Gegend rate ich dringend von der Verwendung von rohen Fäden ab. Ich bevorzuge die Verwendung von Callables y FutureTasks (Aus der Javadoc: "A cancellable asynchronous computation"). Die Integration von Timeouts, das korrekte Abbrechen und das Thread-Pooling der modernen Gleichzeitigkeitsunterstützung sind für mich alle viel nützlicher als ein Haufen roher Threads.

Nachbereitung: Es gibt eine FutureTask Konstrukteur die es Ihnen ermöglicht, Runnables zu verwenden (wenn Sie sich damit am wohlsten fühlen) und dennoch die Vorteile der modernen Gleichzeitigkeitswerkzeuge zu nutzen. Um die Javadoc zu zitieren :

Wenn Sie kein bestimmtes Ergebnis benötigen, können Sie Konstruktionen dieser Art verwenden:

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

Wenn wir also ihre runnable mit Ihrem threadA erhalten wir Folgendes:

new FutureTask<Object>(threadA, null)

Eine weitere Möglichkeit, näher an Runnables zu bleiben, ist ein ThreadPoolExecutor . Sie können die ausführen. Methode, um ein Runnable zu übergeben, das "die angegebene Aufgabe irgendwann in der Zukunft" ausführt.

Wenn Sie versuchen möchten, einen Thread-Pool zu verwenden, würde das obige Codefragment etwa wie folgt aussehen (unter Verwendung der Executors.newCachedThreadPool() Fabrikmethode):

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

45 Stimmen

Dies ist besser als die akzeptierte Antwort IMHO. Eine Sache: der Codeschnipsel, den Sie haben, schließt den Executor nicht und ich sehe Millionen von Fragen, bei denen die Leute das falsch machen und jedes Mal einen neuen Executor erstellen, wenn sie eine Aufgabe starten wollen. es wäre besser als statisches (oder injiziertes) Feld, damit es nur einmal erstellt wird.

8 Stimmen

@artbristol, danke! Ich widerspreche nicht auf den neuen Executor (wir tun, was Sie vorschlagen, in unserem Code). Als ich die ursprüngliche Antwort schrieb, habe ich versucht, einen minimalen Code zu schreiben, der dem ursprünglichen Fragment entspricht. Wir müssen hoffen, dass viele Leser dieser Antworten sie als Ausgangspunkt verwenden. Ich versuche nicht, einen Ersatz für das Javadoc zu schreiben. Ich schreibe quasi Marketingmaterial dafür: Wenn Ihnen diese Methode gefällt, sollten Sie sich all die anderen tollen Dinge ansehen, die wir zu bieten haben...!

7 Stimmen

Ich weiß, ich bin ein bisschen spät dran mit meinem Kommentar, aber der Umgang mit FutureTask direkt ist im Allgemeinen nicht das, was Sie tun wollen. ExecutorService s werden die entsprechenden Future für Sie, wenn Sie submit a Runnable / Callable zu ihnen. Gleiches gilt für ScheduledExecutorService s und ScheduledFuture wenn Sie schedule a Runnable / Callable .

293voto

panzerschreck Punkte 3482

Die Moral von der Geschicht':

Vererben Sie nur, wenn Sie ein bestimmtes Verhalten außer Kraft setzen wollen.

Oder besser gesagt: Es sollte heißen:

Weniger vererben, mehr verknüpfen.

1 Stimmen

Dies sollte immer die Frage sein, wenn Sie ein gleichzeitig laufendes Objekt erstellen! Benötigen Sie die Thread-Objekt-Funktionen überhaupt?

7 Stimmen

Wenn man von Thread erbt, möchte man fast immer das Verhalten der run() Methode.

1 Stimmen

Sie können das Verhalten einer java.lang.Thread durch Überschreiben der run() Methode. In diesem Fall müssen Sie die Methode start() Methode, denke ich. Normalerweise verwenden Sie einfach das Verhalten der java.lang.Thread indem Sie Ihren Ausführungsblock in die run() Methode.

268voto

Rupesh Yadav Punkte 14956

Nun, so viele gute Antworten, ich möchte noch mehr dazu sagen. Dies wird helfen zu verstehen Extending v/s Implementing Thread .
Extends bindet zwei Klassendateien sehr eng aneinander und kann zu ziemlich schwer zu handhabendem Code führen.

Beide Ansätze erfüllen die gleiche Aufgabe, aber es gibt einige Unterschiede.
Der häufigste Unterschied ist

  1. Wenn Sie die Klasse Thread erweitern, können Sie danach keine andere Klasse mehr erweitern, die Sie benötigen. (Wie Sie wissen, ist es in Java nicht möglich, mehr als eine Klasse zu erben).
  2. Wenn Sie Runnable implementieren, können Sie Platz für Ihre Klasse sparen, um jede andere Klasse in der Zukunft oder jetzt zu erweitern.

Allerdings ist eine erheblicher Unterschied zwischen der Implementierung von Runnable und der Erweiterung von Thread ist, 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 wird Ihnen helfen, die Situation besser zu verstehen

//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 Ansatz der Runnable-Schnittstelle wird nur eine Instanz einer Klasse erstellt, die von verschiedenen Threads gemeinsam genutzt wird. Daher wird der Wert des Zählers für jeden einzelnen Thread-Zugriff erhöht.

Beim Thread-Klassen-Ansatz hingegen müssen Sie für jeden Thread-Zugriff eine eigene Instanz erstellen. Daher wird für jede Klasseninstanz ein anderer Speicher zugewiesen und jeder hat einen separaten Zähler, dessen Wert gleich bleibt, was bedeutet, dass kein Inkrement erfolgt, da keine der Objektreferenzen gleich ist.

Wann sollte man Runnable verwenden?
Verwenden Sie die Schnittstelle Runnable, wenn Sie von einer Gruppe von Threads aus auf dieselben Ressourcen zugreifen wollen. Vermeiden Sie hier die Verwendung der Thread-Klasse, da die Erstellung mehrerer Objekte mehr Speicher verbraucht und zu einem großen Leistungs-Overhead führt.

Eine Klasse, die Runnable implementiert, ist kein Thread und 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 planen, die run() Methode und keine anderen Thread-Methoden. Dies ist wichtig, weil Klassen nicht unterklassifiziert werden sollten, es sei denn, der Programmierer beabsichtigt, das grundlegende Verhalten der Klasse zu ändern oder zu verbessern.

Wenn es notwendig ist, eine Oberklasse zu erweitern, ist die Implementierung der Runnable-Schnittstelle besser geeignet als die Verwendung der Thread-Klasse. Denn wir können eine andere Klasse erweitern und gleichzeitig die Runnable-Schnittstelle implementieren, um einen Thread zu erstellen.

Ich hoffe, das wird helfen!

47 Stimmen

Ihr Code ist offenkundig falsch. Ich meine, er tut, was er tut, aber nicht das, was Sie zeigen wollten.

46 Stimmen

Zur Verdeutlichung: Für den runnable-Fall haben Sie die gleiche ImplementsRunnable-Instanz verwendet, um mehrere Threads zu starten, während Sie für den Thread-Fall verschiedene ExtendsThread-Instanzen erstellen, was offensichtlich zu dem von Ihnen gezeigten Verhalten führt. Die 2. Hälfte Ihrer Hauptmethode sollte lauten: 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 noch deutlicher?

0 Stimmen

@zEro Haben zu beobachten, dass in beiden Fällen (implementieren Runnable & erweitern Thread), Es gibt drei unabhängige Threads & im Falle der Erweiterung Thread-Klasse jeder Thread der Ausführung wird dort eigene Instanz verwenden, wo als in implementieren Runnable alle drei Threads gleiche Instanz verwenden, um auf zu arbeiten. Und ja, wir können es so machen, aber wir haben mehr Flexibilität im Fall von Runnable.

90voto

Nidhish Krishnan Punkte 20125

Wenn Sie irgendeine andere Klasse implementieren oder erweitern wollen, dann Runnable Schnittstelle ist am besten geeignet, andernfalls, wenn Sie keine andere Klasse erweitern oder implementieren wollen, dann Thread Klasse ist vorzuziehen.

Der häufigste Unterschied ist

enter image description here

Wenn Sie extends Thread Klasse, danach können Sie keine andere Klasse mehr erweitern, die Sie benötigen. (Wie Sie wissen, ist es in Java nicht möglich, mehr als eine Klasse zu erben).

Wenn Sie implements Runnable können Sie Platz für Ihre Klasse sparen, um eine andere Klasse in der Zukunft oder jetzt zu erweitern.

  • Java unterstützt keine Mehrfachvererbung, d.h. Sie können nur eine Klasse in Java erweitern. Sobald Sie also die Klasse Thread erweitert haben, haben Sie Ihre Chance verloren und können keine andere Klasse in Java erweitern oder erben.

  • In der objektorientierten Programmierung bedeutet die Erweiterung einer Klasse im Allgemeinen das Hinzufügen neuer Funktionen und das Ändern oder Verbessern von Verhaltensweisen. Wenn wir keine Änderungen am Thread vornehmen, verwenden wir stattdessen die Schnittstelle Runnable.

  • Die Schnittstelle Runnable stellt einen Task dar, der entweder von einem einfachen Thread oder Executors oder auf andere Weise ausgeführt werden kann. Daher ist die logische Trennung von Task als Runnable und nicht als Thread eine gute Designentscheidung.

  • Trennen von Aufgabe als Runnable bedeutet, wir können die Aufgabe wiederverwenden und hat auch die Freiheit, es von verschiedenen Mitteln auszuführen. da Sie einen Thread nicht neu starten können, sobald es abgeschlossen ist. wieder Runnable vs Thread für Aufgabe, Runnable ist Gewinner.

  • Der Java-Designer hat dies erkannt und deshalb akzeptieren Executors Runnable als Task und haben einen Worker-Thread, der diese Tasks ausführt.

  • Das Vererben aller Thread-Methoden ist zusätzlicher Overhead, nur um einen Task zu repräsentieren, was mit Runnable leicht möglich ist.

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 sie bitte in den Kommentaren mit. Ich persönlich bevorzuge Runnable gegenüber Thread für dieses Szenario und empfehle die Verwendung der Runnable- oder Callable-Schnittstelle je nach Ihren Anforderungen.

Der wesentliche Unterschied besteht jedoch darin.

Wenn Sie extends Thread Klasse erstellt jeder Ihrer Threads ein eindeutiges Objekt und assoziiert es mit diesem. Wenn Sie implements Runnable wird dasselbe Objekt für mehrere Threads freigegeben.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X