Warum ist Thread keine abstrakte Klasse und start () nicht endgültig?

79

Warum wurde die ThreadKlasse als reguläre Klasse und nicht als abstrakte Klasse implementiert, wobei die run()Methode abstrakt war?

Wird es möglicherweise Probleme geben? Oder hat es irgendeinen Sinn, so zu sein?

Außerdem soll die Thread.start()Methode eine sehr spezifische Methode sein, deren Funktionalität von keiner anderen Klasse implementiert werden kann (wenn ich mich nicht irre). Und daher denke ich, dass das finalSchlüsselwort mehr als jede andere Methode dafür geeignet wäre.

Aber ich kann diese Methode überschreiben und verwenden, wie ich möchte.

public class Test extends Thread {
    public static void main (String... args) {
        Thread test = new Test();
        test.start();
    }

    @Override
    public void run() {
        System.out.println("New thread started...");
    }

    @Override
    public void start() {
        System.out.println("Did anyone tell you I will spawn a new thread??");
    }
}

Es wurde offensichtlich nur gedruckt,

Hat dir jemand gesagt, dass ich einen neuen Thread erzeugen werde?

Gibt es eine andere Möglichkeit zum Überschreiben, als den Techniker, der Sie ersetzt, zu verwirren?

Wenn nicht, warum wurde die Methode in der Thread-Klasse nicht als endgültig deklariert?

Codebender
quelle
Hier rufen Sie method start als Mitglied der Klasse auf ... es wird nicht vom Scheduler aufgerufen ...
CoderNeji
2
Nur zur Klarheit - Sie fragen sich, warum Thread.run () nicht abstrakt ist, da Benutzer es überschreiben müssen, und warum Thread.start () nicht endgültig ist, da es überhaupt keinen Sinn macht, es zu überschreiben? Gute Frage! Ich stelle fest, dass jemand abgestimmt hat - ich frage mich warum?
NickJ
3
Ich wünschte eher, die ThreadKlasse wäre endgültig. Der Konstruktor verwendet bereits eine ausführbare Datei, sodass zum Erstellen eines Threads keine Vererbung erforderlich ist.
Rechtsfalte

Antworten:

50

Warum wurde die Thread-Klasse als reguläre Klasse und nicht als abstrakte Klasse implementiert, wobei die run () -Methode abstrakt war?

Diese Frage läuft darauf hinaus, dass Sie Komposition immer der Vererbung vorziehen sollten.

Wenn die ThreadKlasse als deklariert abstractwäre, müsste die Sprache eine andere Klasse bereitstellen, die daraus erweitert wurde und mit der Programmierer eine erstellen könnten Thread. Ihre Frage wäre dann , warum diese Klasse, die extendsaus Threadnicht abstract. Wenn die Sprache keine andere Klasse bereitstellen würde , die extendsvon stammt Thread, müssten Programmierer ihre eigene Klasse erstellen, die extendvon Threadder run()Methode stammt, und diese überschreiben .

Wenn nicht, warum wurde die Methode in der Thread-Klasse nicht als endgültig deklariert?

Die einzig mögliche Erklärung, die ich geben kann, ist, dass die Entwickler der Sprache einige Anwendungsfälle zum Überschreiben gesehen haben, startals die Klasse in das JDK eingeführt wurde. Die erste Version von Java, die ich verwendet habe, war 1.5, und ich persönlich bin nicht auf einen Anwendungsfall gestoßen, bei dem ich festgestellt habe, dass eine Überschreibung erforderlich ist start. Wie JB Nizet in seiner Antwort feststellte

Wenn Java heute von Grund auf neu gestaltet wurde, besteht eine gute Chance, dass das Design anders ausfällt

CKing
quelle
1
Obwohl Sie die Thread-Klasse nicht erweitern müssen, um einen Thread zu benennen (nur das Aufrufen von Thread.setName () würde ausreichen), ist der Teil, der erklärt, warum die Klasse nicht abstrakt sein kann, sehr klar und präzise ...
Codebender
@ Codebender vereinbart. Ich habe das aus meiner Antwort entfernt, da es kein gutes Beispiel war und auch weil Ihre Frage nicht gefragt hat, wann der Thread erweitert werden soll.
CKing
Es ist ein klares Design; warum sollte es heute anders sein?
Warren Dew
76

Sie können sich natürlich selbst in den Fuß schießen, aber das bedeutet nicht, dass Sie es müssen.

Warum wurde die Thread-Klasse als reguläre Klasse und nicht als abstrakte Klasse implementiert, wobei die run () -Methode abstrakt war?

Da die empfohlene Methode zum Erstellen eines Starts eines Threads nicht darin besteht, Thread zu unterordnen. Die empfohlene Methode besteht darin, a zu definieren Runnableund als Argument an den Thread-Konstruktor zu übergeben:

Runnable r = new Runnable() {
    @Override
    public void run() {
        ...
    }
};
Thread t = new Thread(r);
t.start();

Und daher denke ich, dass das endgültige Schlüsselwort mehr als jede andere Methode dafür geeignet wäre.

Ja und nein. Sie können die Implementierung von start () nicht durch Ihre eigene Implementierung ersetzen, aber Sie können in start () zusätzliche Dinge tun, wenn Sie möchten:

@Override
public void start() {
    System.out.println("Did anyone tell you I will spawn a new thread??");
    super.start();
}

Wenn Java heute von Grund auf neu gestaltet wurde, besteht jedoch eine gute Chance, dass das Design anders ausfällt. Denken Sie daran, dass diese Klasse aus Java 1.0 stammt und weiterhin abwärtskompatibel ist.

JB Nizet
quelle
Ich habe dich in meiner Antwort zitiert. Hoffe das ist gut mit dir :)
CKing
11
Kein Problem. Es ist Open Source.
JB Nizet
2
@NayukiMinase Nr. Schnittstellen waren schon immer in der Sprache. Runnable existiert seit JDK 1.0.
JB Nizet
Hoppla, ich war verwirrt darüber, wie in 1.1 ein neues AWT-Ereignisbehandlungssystem eingeführt wurde, das auf Schnittstellenrückrufen (anstelle von Methodenüberschreibungen) basiert.
Nayuki
1
Faden hat ein sauberes Design; warum sollte es heute anders sein?
Warren Dew
40

Warum Thread.start()nicht final?

Sind Sie sicher, dass Sie es niemals überschreiben möchten?

Class MyThreadFactory implements ThreadFactory {
    @Override
    public Thread newThread(Runnable r) {
        return new Thread(r) {
            @Override
            public void start() {
                LOGGER.info("Starting thread " + this);
                super.start();
            }
        };
    }
}
Solomon Slow
quelle
0

Ich bin der Meinung, dass ein paar Kalibrierungen für die Antworten veröffentlicht werden sollten:

"Bist du sicher, dass du es niemals überschreiben willst?"

Nein ! Ich würde nicht. Das ist wie zu sagen: "Sind Sie sicher, dass Sie diese Variable als privat deklarieren möchten?" Denn wenn ich eine Variable, die ich verwende, als öffentlich deklarieren könnte, ohne befürchten zu müssen, dass andere Entwickler meine Entwurfslogik durcheinander bringen könnten, wäre das Codieren ein Kinderspiel. Einer der wichtigsten Zwecke der OOPS-Konzepte für Umfang, Abstraktion, Polymorphismus, Fehlerbehandlung usw. besteht darin, anderen Entwicklern das Design und die Absichten Ihres Codes mitzuteilen. Wie in der Frage ausgeführt, zwingt Sie niemand, wenn Sie die Startmethode überschreiben super.start () verwenden. @Codebender hat eine Startmethode für einen Thread geschrieben, ohne super.start () zu verwenden, und der Compiler hat sich nie beschwert. Es steht ihm also frei, den gesamten Mechanismus des Threading zu brechen, und der Compiler soll ihn einfach passieren lassen? Die Startmethode des Threads stellt sicher, dass die Ausführungsmethode aufgerufen und zum richtigen Zeitpunkt ausgeführt wird! Es ist entscheidend für das Konzept des Einfädelns. Ich würde mich sehr über eine Korrektur freuen, wenn ich hier etwas verpassen würde. 2.

Da die empfohlene Methode zum Erstellen eines Starts eines Threads nicht darin besteht, Thread zu unterordnen.

OK, wenn Codebender vom Design her erlaubt ist, Thread zu sublassieren und die Startmethode durcheinander zu bringen. Nach dieser Logik ist es das Design, das sich selbst in den Fuß schießt. Nach einer anderen Aussage (der ich voll und ganz zustimme) ist Runnable der empfohlene Weg. Warum erlauben wir dann der Thread-Klasse überhaupt, den Thread ohne Einschränkungen zu instanziieren? Darauf folgt:

Sie können die Implementierung von start () nicht durch Ihre eigene Implementierung ersetzen.

Das unterstützt tatsächlich die Behauptung von Codebender, dass die Startmethode endgültig sein sollte, wenn Sie das sagen .. ^

Der eine Punkt, der gültig ist, wird bereits als Randnotiz erwähnt, ist aber die eigentliche Antwort auf diese Frage

"Rückwärtskompatibilität".

Tatsächlich wurden Verbesserungen sogar erst in JDK 5.0 vorgenommen, als sie viele wichtige Ergänzungen und Erläuterungen zum Java-Parallelitätsmodell enthielten. Wir möchten, dass die Abwärtskompatibilität vollständig unterstützt wird, und deshalb ist die Thread-Klasse immer noch genau so, wie sie zuvor war, obwohl die Runnable-Schnittstelle heutzutage die empfohlene Methode ist.

Auch hier würde ich mich sehr über eine Korrektur freuen, wenn ich etwas verpassen würde.

Utkarsh Srivastava
quelle