4.0 KiB
Nebenläufigkeit
Grundlagen
Computer führen mehr als eine Aufgabe gleichzeitig aus. Auch innerhalb eine Programms können wir quasi gleichzeitige Ausführung von Programmcode programmieren. Diese Art der Programmierung bezeichnen wir als nebenläufig.
Es gibt zwei verschiedene Ausführungseinheiten:
- Prozesse: Besitzen eigene Laufzeitresourcen, am wichtigsten auch einen eigenen Speicherbereich
- Threads: Sind leichtgewichtige Prozesse. Threads existieren in einem Prozess und teilen sich Laufzeitresourcen mit ihm, wie den Speicherbereich und geöffnete Dateien.
Die meisten JVMs laufen in einem einzigen Prozess.
System Threads
Zwei Threads haben wir bereits benutzt. Den main-Thread und den GUI-Thread von JavaFX (application thread
).
Deklaration
In Java gibt es zwei Möglichkeiten einen neuen Thread zu erzeugen. Entweder man erbt von der Klasse Thread
oder man implementiert das Interface Runnable
.
Von Thread
erben
class HelloThread extends Thread {
public void run() {
System.out.println("Hallo aus einem Thread");
}
}
Thread thread = new HelloThread();
thread.start();
- Simpel: Einfache Implementation
- Unflexibel: Nebenläufige Klasse muss immer eine Subklasse von
Thread
sein
Runnable
implementieren
class HelloThread implements Runnable {
public void run() {
System.out.println("Hallo aus einem Thread");
}
}
Thread thread = new Thread(new HelloThread());
thread.start();
- Kompliziert: Etwas schwieriger zu starten
- Flexibel: Die nebenläufige Klasse kann beliebige Superklassen haben
Einen Thread pausieren
Mithilfe der Methode Thread.sleep(long milliseconds) throws InterruptedException
kann ein Thread in seiner Ausführung pausiert werden.
class HelloThread implements Runnable {
public void run() {
System.out.println("Hallo aus einem Thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e ) {
e.printStacktrace();
}
System.out.println("Hallo 1 Sekunde später")
}
}
Einen Thread unterbrechen
Ein Thread thread1
kann von einem anderen Thread thread2
mithilfe der Methode thread1.interrupt()
unterbrochen werden. Wird ein Thread unterbrochen wird der Interrupted-State
gesetzt. Der kann mithilfe der Methode Thread.interrupted()
abgefragt werden. Ist der Thread aktuell pausiert wird die InterruptedException
der Methode sleep
geworfen.
In beiden Fällen kann der Thread selbst entscheiden ob er abbrechen will.
Thread thread = new Thread(new LongRunningThread());
thread.start();
thread.interrupt();
Auf einen Thread warten
Wenn wir aus einem Thread auf die Beendung eines anderen Threads warten wollen, können wir dafür die Methode join()
nutzen.
Thread thread = new Thread(new LongRunningThread());
thread.start();
System.out.println("Jetzt läuft der Thread");
thread.join();
System.out.println("Jetzt ist der Thread fertig");
Lost update problem
class Counter {
private int c = 0;
public void decrement() {
c--;
}
}
Intern wird decrement()
auf jeweils drei Operationen abgebildet:
- Aktuellen Wert von
c
holen - Den ermittelten Wert um 1 dekrementieren
- Das Ergebnis in
c
speichern
Lost update problem
Thread A
und Thread B
wollen beide den Zähler dekrementieren. Das kann dazu führen das folgendes passiert:
Ausgangslage: Das Feld c
hat den Wert 5
.
- Thread
A
: Aktuellen Wert vonc
holen - Thread
B
: Aktuellen Wert vonc
holen - Thread
A
: Den ermittelten Wert um 1 dekrementieren. Ergebnis ist4
. - Thread
B
: Den ermittelten Wert um 1 dekrementieren. Ergebnis ist4
. - Thread
A
: Den ermittelten Wert inc
abspeichern.c
ist jetzt4
. - Thread
B
: Den ermittelten Wert inc
abspeichern.c
ist jetzt4
.