Threads in Sequenzdiagramm darstellen

inthemiddle

Mitglied
Hi, wie kann ich denn Threads in einem Sequenzdiagramm darstellen?
Java:
Thread th = new Thread(() -> {c.g(11, A.this);});
        th.start();

th.start() ist ja ein ganz normaler Methodenaufruf, wird denn dann der Thread in einem Rechteck (wie ein Objekt) th:Thread dargestellt?
 

fhoffmann

Top Contributor
Wichtig ist hier doch, dass die Methode g auf dem Objekt c aufgerufen wird. Dass dies in Java mit Hilfe eines Objects der Klasse Thread geschieht, ist ein Implementierungsdetail.
 

fhoffmann

Top Contributor
Thread th = new Thread(() -> {c.g(11, A.this);});
Dieser Code bedeutet ja eigentlich
- dass du eine anonyme Klasse erstellst, die von java.lang.Thread erbt,
- dass du die Methode run() überschreibst
Die Methode run() wird von der Methode start() aufgerufen. Und die Methode run() ruft dann c.g() auf.

Wenn das Sequenzdiagramm dazu dienen soll, den Ablauf des Codes zu verstehen, würde ich solche Details nicht darstellen (außer - wie mihe7 es erwähnt hat - die Tatsache, dass der Aufruf asynchron ist).

Wenn das Sequenzdiagramm allerdings dazu dienen soll, zu überprüfen, ob du verstanden hast, wie in Java Threads arbeiten, müssen die Details natürlich enthalten sein.
 

mrBrown

Super-Moderator
Mitarbeiter
Dieser Code bedeutet ja eigentlich
- dass du eine anonyme Klasse erstellst, die von java.lang.Thread erbt,
- dass du die Methode run() überschreibst
Die Methode run() wird von der Methode start() aufgerufen. Und die Methode run() ruft dann c.g() auf.
Ne, überschrieben wird Thread dort nicht, es wird nur ein Runnable übergeben, welches dann vom Thread innerhalb von run ausgeführt wird :)
 

fhoffmann

Top Contributor
Ne, überschrieben wird Thread dort nicht, es wird nur ein Runnable übergeben, welches dann vom Thread innerhalb von run ausgeführt wird
Hallo mrBrown,

ich hatte auch überlegt, ob dies so sein könnte. Aber das schien mir zu kompliziert. Was ist den der Vorteil dieser Konstruktion ?
Ich müsste ja etwa folgenden Code generieren:
Java:
class AnonymRunnable implements Runnable {
  public void run() {
    // doSomething()
  }
}
AnonymRunnable anonymRunnable = new AnonymRunnable();
Thread thread = new Thread(anonymRunnable);
Dagegen ist es doch einfacher, folgenden Code zu generieren:
Java:
class AnonymThread extends Thread {
  public void run() {
    // doSomething()
  }
}
Thread thread = new AnonymThread();
 

mrBrown

Super-Moderator
Mitarbeiter
Wieso Code generieren, der fertige, ausführbare Code steht doch bereits dort?

Die Übergabe eines Runnables passt in den meisten Fällen schon aus semantischen Gründen besser: der eigene Code soll zwar in einem Thread ausgeführt werden, ist aber kein Thread.
Außerdem kann man mit Runnables besser arbeiten: Thread zu erweitern koppelt einen an die Thread-Klasse, in vielen Fällen will man aber gar nicht explizit selbst einen Thread starten, sondern viel eher ExecutorServices und ThreadPools nutzen - das ist mit Runnables direkt möglich. Wenn nötig, kann man dabei immer noch explizit den Thread erzeugen, hat aber deutlich mehr Möglichkeiten.
 

mrBrown

Super-Moderator
Mitarbeiter
Und erben Lambdas immer von java.lang.Runnable ?
sorry -aber ich habe mich mit der Theorie von Lambdas noch nicht beschäftigt.
Ne, die können jedes Single-Abstract-Method-Interface implementieren – welches das konkret ist, ist erst durch den "Zieltyp" klar.

Stumpf ausgedrückt sind Lambdas die Reduktion der Implementierung einer normalen Klasse, die das Interface erweitert, auf das minimal nötige:
Java:
class AnonymRunnable implements Runnable {
  public void run() {
    // doSomething()
  }
}

- Klassenname ist egal, Lambdas sind anonym, kann also weg
- das Implementierte Interface ist durch die Nutzung auch eindeutig
- Signatur und Rückgabetyp der Methode sind auch klar, da das Interface nur eine Methode hat, wichtig sind nur Parameter

Wenn man das alles weg lässt, landet man für Runnable bei:
Java:
() -> {}
 

mihe7

Top Contributor
Und erben Lambdas immer von java.lang.Runnable ?
sorry -aber ich habe mich mit der Theorie von Lambdas noch nicht beschäftigt.
Nein. Lambdas sind anonyme Funktionen (analog zu anonymekn Klassen). Du kannst sie überall verwenden, wo ein functional interface erwartet wird, also ein Typ mit genau einer zu implementierenden Methode.

Runnable hat nur eine Methode void run() daher kannst Du ein Runnable mit einem Lambda implementieren: () -> { .... } vorne steht die Parameterliste der Methode.

EDIT: Halte Dich an die Beschreibung von @mrBrown - die ist wesentlich besser.
 

fhoffmann

Top Contributor
Ich habe gerade noch einaml darüber nachgedacht:

Die Klasse Thread
- implementiert das interface Runnable
- und hat ein Member vom Typ Runnable

Wenn ich also
Java:
Thread th = new Thread(() -> {c.g(11, A.this);});
aufrufe, müsste die Methode run() des interfaces Runnable aufgerufen werden, das Thread implementiert, und nicht die Methode des Members.
 

mrBrown

Super-Moderator
Mitarbeiter
Es gibt aber in diesem Fall kein Member (null), also wird die eigene run()-Methode aufgerufen.
In diesem Fall:
Java:
Thread th = new Thread(() -> {c.g(11, A.this);});

Wird doch ein Runnable übergeben, das landet im Member target, und in der run-Methode von Thread gibts dann ein
Java:
if (target != null) {
    target.run();
}
womit dann das übergebene Runnable ausgeführt wird.
 

mrBrown

Super-Moderator
Mitarbeiter
Häh?

Beim Starten eines Threads wird immer die run-Methode dieses Threads aufgerufen.
In der run-Methode des Threads wird dann die run-Methode des übergebenen Runnables aufgerufen, da ist keinerlei Magie oder so beteiligt.
 

mrBrown

Super-Moderator
Mitarbeiter
Thread sieht ganz grob so aus:

Java:
class Thread implements Runnable {
    
    Runnable target;
    
    public Thread() {}
    
    public Thread(Runnable target) {
        this.target = target;
    }
    
    public final void start() {
        this.run();
    }
    
    public void run() {
        if (target != null) {
            target.run()
        }
    }
    
}


Wenn man Thread erweitert und run überschreibt, wird das übergebene Runnable ignoriert und das ausgeführt, womit man die run-Methode überschrieben hat.

Wenn man Thread nicht erweitert, sondern ein Runnable übergibt, wird das übergebene Runnable ausgeführt.


Oben bezog ich mich auf die zweite Variante, dabei wird nichts überschrieben, sondern das Runnable ausgeführt.
 

fhoffmann

Top Contributor
Thread sieht ganz grob so aus:
Das ist mir schon klar.
Ich frage ja nur, ob die eigene run()-Methode überschrieben wird oder ob die run()-Methode des übergebenen Objects vom Typ Runnable aufgerufen wird.
Und ich glaube, dass bei
Java:
Thread th = new Thread(() -> {c.g(11, A.this);});
die eigene Methode run() überscrhieben wird.

Letztlich ist dies eine rein akademische Diskussion - das Ergebnis bleibt das gleiche.
 

mrBrown

Super-Moderator
Mitarbeiter
Und ich glaube, dass bei
Java:
Thread th = new Thread(() -> {c.g(11, A.this);});
die eigene Methode run() überscrhieben wird.
Ne, da wird wirklich nichts überschrieben, es wird einfach nur der Konstruktor aufgerufen, der im Endeffekt die Instanzvariable zuweist, und die run-Methode dieser Instanzvariable wird dann aufgerufen.

Überschrieben wird da nichts, es wird nur das Runnable-Interface in Form des Lambdas "implementiert"
 

mihe7

Top Contributor
Ich frage ja nur, ob die eigene run()-Methode überschrieben wird
Das scheitert schon daran, dass Du zum Überschreiben erstmal eine (ggf. anonyme) von Thread abgeleitete Klasse erstellen müsstest, was hier nicht der Fall ist.

Der Code ist äquivalent zu
Java:
Runnable r = () -> {c.g(11, A.this);}
Thread th = new Thread(r);
Somit wird einfach eine neue Instanz der Klasse Thread erstellt.

Um die run-Methode von Thread zu überschreiben, müsstest Du ja etwas haben wie
Java:
Thread th = new Thread() {
    @Override
    public void run() {
        c.g(11, A.this);
    }
};
 

Neue Themen


Oben