Thread stoppen mit System.in.read()

Gina

Mitglied
Hallo,

ich habe ein Programm geschrieben, dass sich nicht mehr stoppen lässt.

So sieht der Code aus:
Java:
package hiphopSeite244;

import java.util.*;
public class HipHop implements Runnable {

    @Override
    public void run() {
        while(true) {
            String name = Thread.currentThread().getName();
            System.out.println(name);
            Random random = new Random();
            try {
                Thread.sleep(1000 + random.nextInt(2000));
            } catch (InterruptedException e) {
                // TODO: handle exception
            }
        }

    }

    public static void main(String[] args) throws java.io.IOException {
        Thread hip = new Thread(new HipHop());
        Thread hop = new Thread(new HipHop());
        hip.start();
        hop.start();
        System.in.read();
        hip.interrupt();
        hop.interrupt();
        System.out.println("Ende");
        

    }

}

Gedacht hatte ich mir das so, dass die beiden Threads eine zeitlang laufen, und dass der main-Thread solange blockiert bis ich die Enter-Taste drücke. Danach werden die beiden Threads hip und hop unterbrochen und die main-Methode läuft aus. Nur leider funktioniert das Programm nicht so, wie ich mir das überlegt habe und läuft so lange weiter, bis ich Eclipse schließe. Woran könnte es liegen? -Für etwas Unterstützung wäre ich dankbar.

Viele Grüße, Gina
 
ich würd mal sagen das liegt daran das es einfach schlecht ... oder ein deinem fall : falsch ... programmiert ist

soweit ich mir deinen code ansehe macht er genau das was er soll ... nämlich zwei threads starten ... auf einen input warten ... die threads interrupten ... und diese schlucken es einfach und laufen weiter ...


guck dir noch mal ganz genau den programm-ablauf an ... es ist relativ einfach ... und lässt sich mit mehr als nur einer lösung "korrekt" stellen
 
Wenn du die Methode [c]interrupt()[/c] eines Threads aufrufst, wird einfach eine Interruptedexception bei diesem geworfen.
Diese tritt bei deinem HipHop Thread auf während du wartest (während deinem sleep).
Du fängst diese mit dem try-catch-Block auf und fängst die Schleife erneut von vorne an.

-> Deshalb werden deine Threads auch nicht beendet.
 
boa ... gnampf ... kann man nicht einfach mal die fresse halten ... warum müssen es immer alle so direkt raushauen ? ... leute ... das hat nichts mit helfen oder lernen zu tun sondern einfach mit vorgeben von lösungen ...
 
Hallo!

Danke für die Antworten -nun weiß ich schon mal, wo ich schauen muss. Ich habe versucht, das Problem mit einer break-Anweisung in dem catch-Block zu lösen:
Java:
package hiphopSeite244;

import java.util.*;
public class HipHop implements Runnable {

    @Override
    public void run() {
        while(true) {
            String name = Thread.currentThread().getName();
            System.out.println(name);
            Random random = new Random();
            try {
                Thread.sleep(1000 + random.nextInt(2000));
            } catch (InterruptedException e) {
                break;
            }
        }

    }

    public static void main(String[] args) throws java.io.IOException {
        Thread hip = new Thread(new HipHop(), "hip");
        Thread hop = new Thread(new HipHop(), "hop");
        hip.start();
        hop.start();
        System.in.read();
        hip.interrupt();
        hop.interrupt();
        System.out.println("Ende");
        

    }

}
Es funktioniert leider immer noch nicht. Dies waren meine Überlegungen dabei: Das break sollte die umgebende while-Schleife beenden und damit die Methode run zuende führen.
Über die Methode interrupt() habe ich dieses im Grundkurs Java gelesen:
"...sendet ein Unterbrechungssignal an den Thread, für den die Methode aufgerufen wurde. Dieser Thread befindet sich dann im Zustand "unterbrochen". Ist der Thread blockiert durch den Aufruf von sleep, join oder wait, so wird der Zustand "unterbrochen" gelöscht und eine InterruptedException ausgelöst."
Gedacht hatte ich es mir so, dass die Methode interrupt die beiden Threads hip und hop unterbricht und diese Threads dann durch das break beendet werden. Nach dem Drücken der Enter-Taste passiert aber schlichtweg gar nichts.
Für etwas Unterstützung wäre ich dankbar.

Viele Grüße, Gina
 
Hallo 🙂

Bei mir funktioniert dein Code, mit Enter werden beide Threads beendet. Allerdings gibt es eine geringe Wahrscheinlichkeit, dass es nicht klappt: Es kann ja sein, dass sich die Ausführung gerade nicht in Sleep befindet und dann auch keine Exception auftritt - wenn das interrupted-Flag gesetzt ist und Sleep betreten wird, wirft es auch keine Exception. Üblicherweise benutzt man als Bedingung für die Schleife deshalb nicht einfach true, sondern fragt dort ab, ob das Interrupted-Flag noch nicht gesetzt wurde, damit die Schleife dann andernfalls unterbrochen wird.

Edit: Oh sorry, das stimmt gar nicht - wenn das interrupted-Flag beim Betreten von Sleep gesetzt ist, wirft es sofort die Exception. Ich glaube deshalb eher, dass das Problem daran liegt, wie du die Eingabe machst. Funktioniert System.in.read() bei dir denn anderswo? Lass dir mal testweise mit einer Ausgabe direkt nach System.in.read() ausgeben, ob main überhaupt an den Punkt dahinter gelangt.
 
Zuletzt bearbeitet:
Hallo!

Nun habe ich getestet, ob ich den Code nach dem System.in.read() überhaupt erreiche:
Java:
...
System.in.read();
System.out.println("X");
...
Das X wird nie ausgegeben. Ich habe extra noch einmal überprüft, ob ich überhaupt die Enter-Taste genommen habe und das Programm auf einem anderen PC ausprobiert. Das Problem bleibt. Könnte es an Eclipse liegen?

Viele Grüße, Gina
 
Also mal zum Verständnis

mit
Code:
System.in.read();
wartet dein Programm in einem InputStream in der Konsole auf einen Integer.

demnach wird blockierend in einer Schleife darin verweilt bis du diese Eingabe in der Konsole tätigst.

Wenn du die Eingabe getätigt hast und diese bestätigst wird auch dein Programm weiter ausgeführt und dein Text wird ausgegeben.

Allerdings kann
Code:
System.in.read();
eine Exception werfen und gehört in einen Try/Catch block, bzw. sollte die Exception an andere Stelle propagiert werden .

außerdem frage ich mich warum du nicht

1. Einen Scanner zur Eingabe Erfassung nutzt
2. Den Wert den du per Eingabe einlesen willst nirgends zwischenspeicherst um ihn weiterverarbeiten zu können, denn mit deinem Aufruf von
Code:
System.in.read();
wird nichts anderes gemacht als den Wert dann wieder auf der Konsole auszugeben.

[EDIT]oder wird auch nach Bestätigung in der Konsole der Text nicht ausgegeben ?[/EDIT]

btw. selbst wenn du das System.in.read(); nur dazu verwenden solltest um in einer Schleife wartend zu verbringen um das Beenden der Threads zu initiieren , kannst du mit dem Aufruf von interrupt(); lediglich darum "bitten" das die Threads ihre Arbeit einstellen.

Ich könnte dir dazu z.B den Abschnitt Threads sicher beenden , aus dem Buch "Der Weg zum Java Profi" - von Michael Inden empfehlen , der dies wenn auch etwas komplex beschreibt, und aufzeigt warum z.B Threads sich in ihrer Ausführung nicht einfach so beenden lassen, und welche Möglichkeiten bestehen trotzdem das beenden eines Threads anzustoßen.

[TIPP]
Falls Interesse besteht kann ich dir nachher wenn ich vor Ort bin auch gern die Seiten ablichten und hier Posten 😉
[/TIPP]
 
Zuletzt bearbeitet:
Die interrupt-Methode ist doch genau die richtige Methode zum Beenden eine Threads. Dass der Thread dadurch nicht sofort beendet wird, ist richtig und gut so. Ansonsten wäre es nämlich nicht deterministisch, was dann passiert - man weiß ja gar nicht, wo sich der Thread gerade innerhalb des Codes gerade in der Ausführung befindet. Und da jeder Thread bestimmte Wartepunkte wie Thread.sleep(), Object.wait() oder irgendwas anderes blockierendes verwenden sollte, funktioniert das in allen Situation zuverlässig. Außerhalb von Wartepunkte finden nur Berechnungen statt, die üblicherweise nur ein paar Millisekunden dauern.

Die einzige Ausnahme würde ich bei Threads sehen, die durch sehr intensive Berechnungen wirklich lange dauern und trotzdem von außen beendet werden sollen. Aber auch da könnte man in jeder Iteration prüfen, ob das interrupted-Flag gesetzt ist. Wenn man mit Tricks dafür sorgt, dass ein Thread wirklich sofort beendet wird, schreibt man nicht-deterministischen Code.

Ich finde den Code von Gina weiterhin vollständig korrekt, das Problem liegt irgendwo an der Art, wie sie die Eingabe macht, da das System.in bei ihr nach dem Drücken von Enter nicht reagiert.
 
OK, mir scheint ich habe mich unverständlich ausgedrückt ^^

selbstverständlich ist das initiieren eines Thread Endes über den Aufruf von
Code:
.interrupt();
vollkommen in Ordnung, so wie oben beschrieben allerdings eher suboptimal, alleine schon aus gründen nicht vernünftiger Abarbeitung der geworfenen Exceptions.

Der Vorschlag einen Auszug aus dem Buch zu posten diente lediglich der Veranschaulichung wie man es Optimieren könnte, hier wird z.B das beenden des Threads über eine Utility Klasse gehandhabt, welche sich dann auch um die Exceptions kümmert.

[EDIT]
Selbstverständlich ist es auch nicht sinnvoll einen Thread inmitten in seiner Bearbeitung zu Unterbrechen , da dies ja sonst wieder zu Inkonsistenzen führen könnte.
[/EDIT]

Ansonsten Spricht nichts dagegen, auch wenn ich nicht in einer endlos Schleife darauf warten würde das jemand mit Enter diese unterbricht, wie gesagt es gibt schönere Lösungsansätze das Flag für den Interrupt zu setzen 🙂.

Falls dennoch Interesse besteht such ich den Anschnitt gerne mal raus
 
Zuletzt bearbeitet:
Hm.. Mich interessiert, warum du das obige Vorgehen suboptimal findest. Das Thread.sleep liefert einen eindeutigen Unterbrechungspunkt für die Schleife - wenn der Thread durch interrupt() unterbrochen wird, wird er also immer deterministisch während des sleep() unterbrochen. Die Exception signalisiert in diesem Fall diese Unterbrechung. Da der Thread bei Unterbrechung die Schleife verlassen soll, hat sie dort ein break geschrieben - das ist doch genau das Verhalten, das der Thread dann zeigen soll. Was würdest du dort denn sonst machen? Das Informieren des Benutzers oder die Ausgabe eines Stack-Traces sind in diesem Fall meiner Meinung nach unangebracht, da die Exception nicht unerwartet kam, sondern hier gezielt zur Signalisierung benutzt wird. Ich weiß, dass Exceptions nur für unerwartete Situation und nicht für das normale Programmverhalten benutzt werden sollten. Allerdings ist es aus Sichts des Sleep-Aufrufs ja tatsächlich ein Sonderfall, dass er "plötzlich" unterbrochen wird, deshalb finde ich das in Ordnung. Und im Übrigen gibt die Java-API das nunmal so vor. Es gab vorher ja mit stop, suspend usw. einen anderen Mechanismus, der dann durch diesen ersetzt wurde.

Als andere Variante fällt mir höchstens ein, dass man ein eigenes Flag einführt, das jemand außerhalb des Threads setzen kann, um den Thread zum Aufhören zu bitten. Dieses würde man dann wohl in der while-Schleife abfragen, um mit der Ausführung dann abzuschließen. Der Abbruchpunkt wäre dann auch deterministisch, nämlich nach der Schleife. Ein Nachteil wäre dann, dass der sleep-Aufruf nicht unterbrochen wird, und der Thread dadurch in der Schleife lange pausieren kann, bis er das Signal bemerkt. Und außerdem hätte man dann nur das interrupt-Flag nachprogrammiert.

Mich würde also interessieren, warum das obige suboptimal ist und wie du es verbessern würdest - insbesondere auch, welche Vorteile dadurch entstehen.
 
Vernünftigerweise würde man es wohl so wie unten implementieren, d.h. den Inhalt des
Schleifenkörpers in das try-catch packen und im catch das Interrupt-Flag
wiederherstellen da dieses "gelöscht" wird und somit vll. ein "höherer" Thread nichts von der
Unterbrechung mitbekommt.

Java:
	@Override
	public void run() {
		while (!Thread.currentThread().isInterrupted()) {
			try {
				Thread.sleep(10000);
			} catch (InterruptedException e) {
				Thread.currentThread().interrupt();
			}
		}
	}
 
Zuletzt bearbeitet:
Hallo,

ich habe mein Programm nun zum Laufen (und zum Anhalten) gebracht. Das Problem war, dass ich nicht beachtet habe, dass man bei Eclipse den Focus dann auch in den Konsolenbereich legen muss. Das passiert mir nicht noch einmal.

Viele Grüße, Gina


@Danke-Button: wo finde ich denn diesen Button?
 

Zurück
Oben