pp.01.03-EndThread
Lebenszyklus von Threads und Interrupts
- Projekt:
pp01.03-EndThread
- Bearbeitungszeit: 20 Minuten
- Musterlösung: 20 Minuten
- Kompatibilität: mindestens Java SE 19
Das Beenden von Threads ist heikel und wird oft falsch gemacht. Im folgenden Beispiel erhalten Sie ein Muster für korrektes Beenden, das Sie jederzeit in eigenen Beispielen reproduzieren können sollten.
Die Grundidee ist, eine boolean
-Instanzvariable zu verwenden, die als Ende-Signal-Flag benutzt wird um zu markieren, dass der Thread terminieren soll. Das Flag wird dann im Programmfluss des Threads (im Beispiel hier ist es eine while
-Schleife) kontrolliert und ggf. zu einem Aufräumteil verzweigt, nach dem der Thread (btw. die Methode run
des Threads) terminiert.
Außerdem geht es in diesem Beispiel darum, wie mit etwaigen nicht behandelten Runtime-Exception umgegangen werden kann. Dazu wird im Runner
eine “Division durch 0”-Exception provoziert, die durch eine Routine abgefangen wird, die “von außen” am Thread angebracht wird, der das Runnable
Task
ablaufen lassen wird.
Quellcode von pp.Task
package pp;
public class Task implements Runnable {
private volatile Thread self;
private volatile boolean stopped = false;
public void stopRequest() {
this.stopped = true;
if (this.self != null) {
this.self.interrupt();
}
}
public boolean isStopped() {
return this.stopped;
}
@Override
public void run() {
this.self = Thread.currentThread();
// 1. Initialisierungsphase
var i = 10;
while (!isStopped()) {
// 2. Arbeitsphase
System.out.println("i=" + i);
try {
Thread.sleep(1000 / i--);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// 3. Aufräumphase
System.out.println("fertig.");
}
}
Quellcode von pp.Runner
package pp;
public class Runner {
public static void main(String... args) {
var task = new Task();
var thread = new Thread(task);
thread.setUncaughtExceptionHandler((t, e) -> {
System.err.println("Unhandled Exception: " + e.getMessage());
System.err.println(" Thread: " + t.threadId() + " - " + t.getName());
System.err.println(" Thread State: " + t.getState());
e.printStackTrace(System.err);
});
thread.start();
}
}
Aufgaben
Analysieren Sie
Task
. Wie lange wird mitThread.sleep
gewartet, bis es zur Exception kommt?Analysieren Sie den Quellcode von
Runner
und lassen SieRunner
laufen. Es sollte eine Exception auftreten. Wo wird sie behandelt?Ändern Sie
Runner
so, dass nach dem Start vontask
ein weiterer Thread erzeugt und gestartet wird, der dieTask
-Instanztask
von außen beendet.Tipp: Sparen Sie unnötige Tipparbeit und verwenden Sie einen Lambda-Ausdruck, um das Functional Interface (
run()
) vonRunnable
zu füllen, den Sie einfach einer neuenThread
-Instanz übergeben können, die Sie sogleich starten (ein “Einzeiler”).Es sollte eine Exception auftreten. Wo wird sie behandelt?
Warum ist es erforderlich in
stopRequest()
vonTask
dem Thread vonTask
, dessen Referenz inself
gespeichert ist, mitinterrupt()
ein Signal zu schicken, ob wohl in die Hauptschleife vonrun()
doch dadurch abgebrochen wird?