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

20voto

Mit der Veröffentlichung von Java 8 gibt es nun eine dritte Möglichkeit.

Runnable ist eine funktionale Schnittstelle Das 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 eine ExecutorService und eine Methodenreferenz:

executor.execute(runner::run)

Diese sind nicht nur viel kürzer als Ihre Beispiele, sondern bieten auch viele der in anderen Antworten genannten Vorteile der Verwendung von Runnable über Thread wie z. B. die alleinige Verantwortung und die Verwendung der Komposition, da Sie das Verhalten des Threads nicht spezialisieren. Dieser Weg vermeidet auch die Erstellung einer zusätzlichen Klasse, wenn Sie nur eine Runnable wie Sie es in Ihren Beispielen tun.

1 Stimmen

Diese Antwort bedarf einer Erklärung. Nach einigem Grübeln komme ich zu dem Schluss, dass () -> {} soll die benutzerdefinierte Logik darstellen, die jemand benötigt? Es wäre also besser zu sagen als () -> { /* Code here */ } ?

18voto

starblue Punkte 53167

Die Instanziierung einer Schnittstelle sorgt für eine sauberere Trennung zwischen Ihrem Code und der Implementierung von Threads, daher würde ich in diesem Fall die Implementierung von Runnable vorziehen.

17voto

Raman Gupta Punkte 1430

Der Unterschied zwischen der Erweiterung des Threads und der Implementierung von Runnable sind:

enter image description here

13voto

AntonyM Punkte 1582

Jeder hier scheint zu denken, dass die Implementierung von Runnable ist der Weg zu gehen, und ich nicht wirklich mit ihnen zu widersprechen, aber es gibt auch ein Fall für die Erweiterung Thread meiner Meinung nach, in der Tat haben Sie Art von es in Ihrem Code demonstriert.

Wenn Sie Runnable implementieren, dann hat die Klasse, die Runnable implementiert, keine Kontrolle über den Thread-Namen, es ist der aufrufende Code, der den Thread-Namen festlegen kann:

new Thread(myRunnable,"WhateverNameiFeelLike");

aber wenn Sie Thread erweitern, können Sie dies innerhalb der Klasse selbst verwalten (so wie Sie in Ihrem Beispiel den Thread 'ThreadB' nennen). In diesem Fall Sie:

A) könnte für die Fehlersuche einen nützlicheren Namen erhalten

B) erzwingen, dass dieser Name für alle Instanzen dieser Klasse verwendet wird (es sei denn, Sie ignorieren die Tatsache, dass es ein Thread ist und tun das oben genannte mit ihm, als ob es ein Runnable ist, aber wir sprechen hier über Konvention in jedem Fall so kann ignorieren, dass Möglichkeit ich denke).

Sie können sogar einen Stack-Trace der Erstellung des Threads nehmen und diesen als Thread-Namen verwenden. Dies mag seltsam erscheinen, aber je nachdem, wie Ihr Code strukturiert ist, kann es für die Fehlersuche sehr nützlich sein.

Dies mag wie eine Kleinigkeit erscheinen, aber wenn Sie eine sehr komplexe Anwendung mit vielen Threads haben und plötzlich alles "stehen geblieben" ist (entweder aus Gründen eines Deadlocks oder möglicherweise wegen eines Fehlers in einem Netzwerkprotokoll, der weniger offensichtlich ist - oder aus anderen endlosen Gründen), dann ist es nicht immer sehr nützlich, einen Stackdump von Java zu erhalten, in dem alle Threads "Thread-1" genannt werden, Thread-2','Thread-3' heißen, ist nicht immer sehr nützlich (es hängt davon ab, wie Ihre Threads strukturiert sind und ob Sie anhand des Stack Trace erkennen können, welcher Thread welcher ist - was nicht immer möglich ist, wenn Sie Gruppen von mehreren Threads verwenden, die alle denselben Code ausführen).

Sie könnten das oben Gesagte natürlich auch auf generische Weise tun, indem Sie eine Erweiterung der Thread-Klasse erstellen, die ihren Namen auf einen Stack-Trace ihres Erstellungsaufrufs setzt, und diese dann mit Ihren Runnable-Implementierungen anstelle der Standard-Java-Thread-Klasse verwenden (siehe unten). Kontext-spezifische Informationen, die im Thread-Namen für die Fehlersuche nützlich wären (ein Verweis auf eine der vielen Warteschlangen oder Sockets, die er zum Beispiel verarbeiten könnte, in welchem Fall Sie es vorziehen könnten, Thread speziell für diesen Fall zu erweitern, so dass Sie den Compiler dazu zwingen können, Ihnen (oder anderen, die Ihre Bibliotheken verwenden) bestimmte Informationen zu übergeben (z. B. die Warteschlange/den Socket, die/den er verarbeitet). z. B. die betreffende Warteschlange/Socket) zur Verwendung im Namen).

Hier ist ein Beispiel für einen generischen Thread mit dem Namen der aufrufenden Stapelverfolgung:

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]

0 Stimmen

CHao Worauf wollen Sie hinaus? Sie könnten Ihren obigen Code während der Ausführung des Threads nicht verwenden, um einen Stacktrace der Thread-Erstellung zu erhalten (stattdessen würden Sie einen einfachen Namen oder bestenfalls einen Stacktrace des Thread-Starts erhalten), aber durch die Unterklassifizierung von Thread können Sie genau das tun und es erzwingen, wobei sogar weitere kontextspezifische Informationen erforderlich sind, wodurch Sie ein konkreteres Verständnis dafür erhalten, welcher Thread ein Problem haben könnte.

8 Stimmen

Mein Punkt ist, dass "Wenn Sie Runnable implementieren, dann hat die Klasse, die Runnable implementiert, keine Kontrolle über den Threadnamen..." offensichtlich falsch ist. Eine Klasse, die Runnable kann in der Tat den Threadnamen kontrollieren, da der Thread, der den Code ausführt, per Definition der aktuelle Thread ist (und jeder Code, der die Sicherheitsprüfungen besteht die Kontrolle über Thread-Namen hat). Wenn man bedenkt, dass du die Hälfte deines Beitrags dem Thema "OMG, was ist mit Thread-Namen!" widmest, scheint das eine ziemlich große Sache zu sein.

0 Stimmen

Der Name des Themas? Nichts hindert Sie daran, auch die Thread-Klasse zu erweitern.

12voto

Jörg Punkte 2314

Da dies ein sehr beliebtes Thema ist und die guten Antworten überall verstreut sind und sehr ausführlich behandelt werden, hielt ich es für vertretbar, die guten Antworten der anderen in einer übersichtlicheren Form zusammenzustellen, damit Neulinge von vornherein einen leichten Überblick haben:

  1. Normalerweise erweitert man eine Klasse, um Funktionen hinzuzufügen oder zu ändern. So, wenn Sie nicht wollen a überschreiben. cualquier Verhalten bei Gewinden und verwenden Sie dann Runnable.

  2. In demselben Licht, wenn Sie nicht brauchen a erben Thread-Methoden, können Sie auf diese Overhead durch die Verwendung von Runnable.

  3. Einzelvererbung : Wenn Sie Thread erweitern, können Sie nicht von einer anderen Klasse erweitern, wenn Sie das also tun wollen, müssen Sie Runnable verwenden.

  4. Es ist ein gutes Design, die Domänenlogik von den technischen Mitteln zu trennen, in diesem Sinne ist es besser, einen Runnable Task zu haben Isolierung Ihr Aufgabe von Ihr Läufer .

  5. Sie können ausführen. das gleiche Runnable Objekt mehrere Male Ein Thread-Objekt kann jedoch nur einmal gestartet werden. (Vielleicht der Grund, warum Executors zwar Runnables, aber keine Threads akzeptieren).

  6. Wenn Sie Ihre Aufgabe als Runnable entwickeln, haben Sie die ganze Flexibilität, wie man sie jetzt und in Zukunft nutzen kann . Sie können es gleichzeitig über Executors, aber auch über Threads laufen lassen. Und Sie könnten es auch nicht-gleichzeitig innerhalb desselben Threads verwenden/aufrufen, wie jeden anderen gewöhnlichen Typ/Objekt.

  7. Dies macht es auch einfacher, die getrennt Aufgabenlogik und Gleichzeitigkeit Aspekte in Ihr Einheitstests .

  8. Wenn Sie sich für diese Frage interessieren, sind Sie vielleicht auch an der Unterschied zwischen Callable und Runnable .

0 Stimmen

@Pino Ja, Thread selbst ist auch ein Runnable. Wenn man ihn jedoch erweitert, um ihn einfach als Runnable zu verwenden, was ist dann der Sinn? Warum nicht einfach eine einfache Runnable ohne den ganzen Ballast verwenden. Ich würde also argumentieren, dass, wenn Sie Thread erweitern, würden Sie es auch ausführen, indem Sie seine Start-Methode, die nur einmal verwendet werden kann. Das ist der Punkt, den Nidhish-Krishnan in seiner Antwort ansprechen wollte. Beachten Sie, dass meine Antwort nur eine Zusammenstellung oder kurze Zusammenfassung anderer Antworten hier ist.

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