Neustart von run() in einem Runnable

Es stellt sich die Frage, wie ich am Ende eines Spiels in der Game-Loop am geschicktesten diese neu starte, wenn der Benutzer ein weiteres Spiel wünscht.

Der Ablauf ist grob der folgende:

Irgendwo beim Start des Programms:

        gameLoop = new GameLoop(...parameter...);
        Thread thread = new Thread(gameLoop);
        thread.start();
    }

In GameLoop:

    public void run() {
        [...]
        while (running) {
            [...]
            if (... Bedingung für Spielende ...) {
                running = false;
                gameOver();
            }
        }
    }

in gameOver() stellt sich dann die Frage, wie denn - so der Benutzer es wünscht - neu gestartet werden soll. Zunächst stellen wir fest:

[ol]
[li] Der Aufruf von gameOver() findet natürlich in dem Thread der Game-Loop statt.
[/li][li] Nach Beenden von gameOver() wird run() beendet (weil running auf false gesetzt wurde) und damit endet auch der Thread, wenn man in ihm nichts mehr tut.
[/li][/ol]

Mir fallen da nun folgende Möglichkeiten ein:

[ol]
[li] Einfach run() neu aufrufen. Wir sind ja noch in dem Extrathread für die Game-Loop. Initialisierungen für ein neues Spiel finden zu Beginn von run() statt.
[/li][li] Einen neuen Thread starten, analog zu dem, was beim Start des Programms gemacht wurde. Wirkt irgendwie unfein, wenn aus der Game-Loop eine Game-Loop eine Game-Loop eine Game-Loop gestartet wird. Andererseits laufen die alten Game-Loops ja aus und die Threads werden beendet. Problem hier: Will die Logik die Game-Loop beenden (weil der Benutzer das Fenster schließt o.ä,), wird auf einem alten GameLoop-Objekt quit() (setzt running auf false) aufgerufen, entweder müsste ich dann das neue irgendwie übergeben, oder die Aufforderung zum Beenden durchreichen (wodurch all die alten Game-Loop-Threads also doch noch leben müssten), das ist alles irgendwie unpraktisch. Edit: Stimmt nicht, das gilt ja nur, falls neue GameLoop-Objekte erzeugt würden. Das muss ja aber nicht sein, man könnte immer wieder das gleiche GameLoop-Objekt starten.
[/li][li] Einen Rückgabewert generieren, aus dem in run() entnommen wird, ob running wirklich auf false gesetzt werden sollte. Eine Initialisierung für den neuen Spielbeginn müsste dann natürlich erfolgen.
[/li][li] running nicht in run() auf false setzten, sondern in gameOver() (oder eben auch nicht). Eine Initialisierung für den neuen Spielbeginn müsste dann natürlich hier ebenfalls erfolgen.
[/li][/ol]

Gibt es da eventuell klare Designregeln, welches dieser (oder noch anderer) Vorgehen da am geeignetsten ist? Oder scheidet eines oder mehrere aus Gründen aus, die mir gerade nicht ersichtlich sind?

Verstehe die Problematik nicht ganz.
Man kann zwar den Thread z.B. per Endlosschleife am Leben und darin das neue Spiel laufen lassen.
Was spricht aber dagegen einfach einen neuen Thread zu erstellen? Das Runnable kann man ja wenn man mag wieder verwenden.
Die Initialsierung bzw. den Reset irgendwelcher Parameter würde ich grundsätzlich immer zu Beginn der run anstoßen.

Ist mir auch gerade klar geworden, siehe Edit.

Wäre denn dann das Vorgehen 2 das sauberste? Ich frage mich halt, warum ich einen neuen Thread brauche, ich kann im alten ja run() auch einfach wieder aufrufen (Vorgehen 1). Spricht da etwas dagegen?

Das mache ich im Moment auch so. Würde man es an anderer Stelle ebenfalls benötigen, könnte ich es aber auch in eine Methode auslagern.

Ich würde diese Variante bevorzugen.
Innerhalb der run dieselbe rekursiv aufrufen halte ich für keine gute Idee, zumal die Entscheidung, ob ein neues Spiel gestartet werden soll ausserhalb des Threads durch den Anwender (i.d.R im EDT) getroffen wird.

Sobald es mehrere Parameter betrifft würde ich das sowieso in eine Methode auslagern. Ist übersichtlicher und evtl. sollen später auch mal gespeicherte Spielstände geladen werden.

Gut, dann werde ich die Variante 2 nehmen.

[QUOTE=_Michael;61149]Ich würde diese Variante bevorzugen.
Innerhalb der run dieselbe rekursiv aufrufen halte ich für keine gute Idee, zumal die Entscheidung, ob ein neues Spiel gestartet werden soll ausserhalb des Threads durch den Anwender (i.d.R im EDT) getroffen wird.[/QUOTE]
Richtig. Nur fällt die Entscheidung, dass der Benutzer danach gefragt werden soll, in der Game-Loop. Diese wird also irgendwie mit einem invokeAndWait den Benutzer im EDT befragen und danach abhängig vom Ergebnis fortfahren.

Wollte ich das vermeiden, müsste ich im EDT immer mal wieder überprüfen, ob der Thread mit der Game-Loop noch lebt und wenn nicht dann dort den Benutzer fragen und einen neuen Thread starten. Von mir aus auch mit einem neuen GameLoop-Objekt. Das scheint mir aber irgendwie gekrückt. müsste ich (mit invokeLater) die Logik darüber informieren, dass das aktuelle Spiel beendet wurde.

Das ist wohl die sauberste Lösung, das ganze aus der GameLoop in die Logik (im EDT) zu verlegen.

Bisher ist es noch sehr übersichtlich, aber das hatte ich eh vor.

Aus der run() heraus wieder run() aufzurufen fände ich etwas unschön. Wenn der Benutzer das ein paar tausend mal macht, bekommt man einen StackOverflowError :o) Aber im Ernst: Der Stack wird dadurch immer voller. Wenn man nach 5 Starts eine Exception bekommt, sieht man das dann auch


Exception ...
   at SomeClass.someMethod()
   at MainClass.run()
   at MainClass.run()
   at MainClass.run()
   at MainClass.run()
   at MainClass.run()

Ich denke, je nachdem, wie “alles drumherum” so aussieht, könnte man einfach einen neuen Thread erstellen und starten, oder gleich von vornherein den Thread darauf auslegen, ewig zu laufen. Also GROB sinngemäß: AUSSEN um deine while (running)-Schleife noch eine while (shouldNotExitIMeanReallyYouKnow)-Schleife machen.

Ich hab es jetzt so gemacht, dass nach Spielende die Logik im EDT informiert wird und diese dann den Benutzer fragt und einen neuen Thread mit einer neuen GameLoop startet.