12. Threads in Java Sequentielle Abarbeitung (1) Sequentielle

Werbung
12. Threads in Java
Einführendes Beispiel
12. Threads in Java
12. Threads in Java
Einführendes Beispiel
Sequentielle Abarbeitung (2)
Ein Thread ist eine Folge von Anweisungen, die unabhängig von anderen Threads
parallel zu diesen ausgeführt wird.
• Jeder Thread hat seinen eigenen Stack um lokale Variablen anzulegen und Methoden aufzurufen.
• Alle Threads eines Prozesses teilen sich den Adressbereich des Prozesses (keine
Interprozesskommunikation notwendig).
• Man nennt Threads auch leichtgewichtige Prozesse.
public class ABCPrinter {
public void run() {
for (char c=’A’ ; c<= ’Z’ ; c++) {
System.out.print(c);
Machmal.eineSekundeNix();
}
}
public void start() {
run();
}
}
Liefert die Ausgabe: ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ
☞ Sequentielle Abarbeitung
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
12. Threads in Java
258
Einführendes Beispiel
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
12. Threads in Java
260
Einführendes Beispiel
Beispiel: Thread (1)
Sequentielle Abarbeitung (1)
Wir erzeugen zwei gleichartige Objekte der Klasse ABCPrinter, deren Methode
start() die Großbuchstaben auf der Konsole ausgibt:
Aus ABCPrinter machen wir ABCThread, indem wir von der Klasse java.lang.Thread
ableiten.
public class MehrmalsP {
public static void main(String[] args) {
ABCPrinter p1 = new ABCPrinter();
ABCPrinter p2 = new ABCPrinter();
Die Klasse Thread hat auch schon eine Methode start(), die genau das macht,
was wir benötigen, nämlich die Methode run() aufrufen.
public class ABCThread extends Thread {
public void run() {
for (char c=’A’ ; c<= ’Z’ ; c++) {
System.out.print(c);
Machmal.eineSekundeNix();
}
}
}
p1.start();
p2.start();
}
}
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
259
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
261
12. Threads in Java
Einführendes Beispiel
12. Threads in Java
Beispiel: Thread (2)
Threads in Java
Threads in Java (2)
In unserem main-Methode benutzen wir nun ABCThread statt ABCPrinter.
• Entscheidend für die Implementierung eines Threads ist nicht die Klasse Thread
sondern die Schnittstelle java.lang.Runnable.
• Runnable definiert eine (abstrakte) Methode run().
• Nur Klassen, die Runnable implementieren, können einen Thread realisieren.
public class MehrmalsT {
public static void main(String[] args) {
ABCThread p1 = new ABCThread();
ABCThread p2 = new ABCThread();
Konsequenz: Zwei Möglichkeiten zur Implementierung von Threads:
p1.start();
p2.start();
}
}
Dies liefert die Ausgabe: AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ
• Thread implementiert Runnable, also leiten wir von Thread ab und überschreiben die Methode run().
• Wir implementieren Runnable in einer eigenen Klasse und lassen Objekte dieser
Klasse von Objekten der Klasse Thread kontrollieren.
☞ parallele Abarbeitung
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
12. Threads in Java
262
Threads in Java
Threads in Java (1)
12. Threads in Java
264
Threads in Java
Beispiel: Runnable (1)
• Die parallel auszuführenden Anweisungen müssen in einer Methode run() enthalten sein
• oder von run() aus aufgerufen werden.
• Die Methode start() sorgt dafür, dass run() nebenläufig ausgeführt wird.
• Ruft man run() dagegen direkt auf, entsteht keine Nebenläufigkeit.
• Die Klasse Thread verfügt bereits über eine Methode start().
• und eine Methode run() (die allerdings nichts tut).
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
263
Hier die zweite Möglichkeit: Wir nutzen implements statt extends.
public class ABCRunnable implements Runnable {
public void run() {
for (char c=’A’ ; c<= ’Z’ ; c++) {
System.out.print(c);
Machmal.eineSekundeNix();
}
}
}
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
265
12. Threads in Java
Threads in Java
Beispiel: Runnable (2)
public class MehrmalsR {
public static void main(String[] args) {
Thread t1 = new Thread(new ABCRunnable());
Thread t2 = new Thread(new ABCRunnable());
t1.start();
t2.start();
}
}
12. Threads in Java
266
Threads in Java
Die Klasse Thread
Klassenmethoden(u.a.):
• public static Thread currentThread()
liefert eine Referenz auf den Thread, der gerade ausgeführt wird.
• public static void yield()
lässt den Thread, der gerade ausgeführt wird, kurz pausieren, um andere Threads
zum Zuge kommen zu lassen.
• public static void sleep(long millis)
der Thread, der gerade ausgeführt wird, pausiert für millis Millisekunden.
InterruptedException möglich, dabei wird das Abbruch-Flag zurückgesetzt.
• public static boolean interrupted()
liefert true, wenn beim Thread, der gerade ausgeführt wird, das Abbruch-Flag
gesetzt ist.
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
12. Threads in Java
268
Threads in Java
Beispiel: sleep
Konstruktor (u.a.):
• public Thread(Runnable target)
Bei Aufruf von start() wird die run-Methode von target nebenläufig ausgeführt.
Instanzmethoden (u.a.):
• public void start()
• public void run()
• public final boolean isAlive()
liefert true, wenn der Thread gestartet aber noch nicht beendet ist.
• public void interrupt()
setzt das Abbruch-Flag des Threads.
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
Threads in Java
• public boolean isInterrupted()
liefert true, wenn das Abbruch-Flag gesetzt ist.
In unserem main-Methode erzeugen wir nun ABCRunnable und steuern diese Objekte mit Hilfe von Objekten der Klasse Thread.
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
12. Threads in Java
public class Machmal {
public static void eineSekundeNix() {
try {
Thread.sleep(1000);
}
catch(InterruptedException e) { }
}
}
Die Methode sleep() kann eine InterruptedException auslösen.
Das Abbruch-Flag des Threads wird dabei zurückgesetzt.
267
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
269
12. Threads in Java
Threads in Java
12. Threads in Java
Threads vorzeitig beenden
Scheduling
• Threads müssen nicht ihre run-Methode komplett abarbeiten. Vielleicht ist in der
run-Methode auch mit Absicht eine Endlosschleife programmiert.
• Idealerweise nutzen wir die Instanzmethoden interrupt() und isInterrupted().
• Man beachte: Wenn sleep unterbrochen wird, wird eine Exception ausgelöst und
das Abbruch-Flag zurückgesetzt.
• Wenn das Abbruch-Flag gesetzt sein soll, muss deshalb im Exception-Handler
interrupt() aufgerufen werden.
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
12. Threads in Java
Threads in Java
270
Threads in Java
• Ein Scheduler verteilt die zur Verfügung stehende Prozessorzeit auf die einzelnen
Threads.
• Das Scheduling ist nicht spezifiziert, sondern hängt von der virtuellen Maschine
ab.
• Es ist aber sichergestellt, dass ein Thread von höherer Priorität durchschnittlich
mehr Prozessorzeit erhält.
• präemptives Scheduling: Die Threads werden unterbrochen.
• Ein Thread erhält zunächst die Priorität des Threads, der ihn erzeugt. Der mainThread hat die Priorität 5 (NORM PRIORITY).
• Methoden zur Steuerung der Priorität: getPriority und setPriority.
• Höchste Priorität gleich 10 (MAX PRIORITY), niedrigste gleich 1 (MIN PRIORITY).
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
12. Threads in Java
272
Synchronisation
Lebenszyklus eines Threads
Synchronisation
• Nach new ist der Thread erzeugt.
• Mit start() wird der Thread ausführbar.
• Mit run() kann der Thread laufend werden. Dieser Zustand ist ein Spezialfall von
ausführbar.
• Der Thread läuft i.d.R. nicht permanent, sondern wird zwischendurch suspendiert,
z.B. durch yield(). Dann ist er nur noch ausführbar, aber nicht laufend.
• Mit der Terminierung von run() ist der Thread beendet.
• Im Zustand ausführbar kann durch sleep() oder wait() der Thread in den Zustand nicht ausführbar überführt werden.
• Mit Hilfe von notify() wird ein nicht ausführbarer Thread wieder ausführbar.
• Ein Thread schreibt Werte in ein Objekt, ein anderes liest Werte aus dem selben
Objekt.
• Wird der schreibende Thread unterbrochen, bevor er den Schreibvorgang komplett
abgeschlossen hat, liest der andere Thread inkonsistente Werte.
• Leser/Schreiber-Problem
• Mit Hilfe von isAlive kann ermittelt werden, ob ein Thread in den Zuständen
ausführbar oder nicht ausführbar ist.
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
271
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
273
12. Threads in Java
Synchronisation
12. Threads in Java
Synchronisation
Beispiel: Leser/Schreiber (1)
Sperren, Monitor
• Das Problem des letzten Beispiels können wir prinzipiell durch Sperren (Locks)
lösen.
• Immer wenn ein Thread die Methode naechster() betritt, wird diese Methode
für alle anderen Threads gesperrt.
• Threads, die eine gesperrte Methode betreten wollen, werden solange supendiert,
bis die Sperre freigegeben wird.
• Die Sperre wird freigegeben, sobald der ausführende Thread die Methode
naechster() verlassen hat.
• Nun kann ein anderer Thread in die Methode naechster() eintreten. Damit
sperrt er diese Methode wiederum für alle anderen Threads.
public class Sequenz {
private static int zaehler = 0;
public static void nachster() {
int zahl = zaehler;
eine Anweisung die lange dauert ...
System.out.print(" " + zahl);
zaehler++;
}
}
• Fazit: maximal ein Thread soll sich in der Methode naechster() befinden (kritischer Abschnitt).
• In der Programmierung bezeichnet man dies als Monitor.
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
12. Threads in Java
274
Synchronisation
12. Threads in Java
276
Synchronisation
Monitor mit synchronized
Beispiel: Leser/Schreiber (2)
public class ZaehlThread extends Thread {
public void run() {
for (;;)
Sequenz.naechster();
}
}
• In Java ist es leicht, einen Monitor zu implementieren.
• Mit dem Schlüsselwort synchronized erreichen wir, dass sich maximal ein
Thread in der durch synchronized gekennzeichneten Methode befinden darf.
synchronized public static void naechster() { ... }
Damit wäre gewährleistet, dass unsere ZaehlThreads eine korrekte Sequenz ausgeben:
Wie sieht die Ausgabe u.U. aus, wenn zwei ZaehlThreads parallel laufen?
0 1 2 2 4 5 6 6 8 ...
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
0 1 2 3 4 5 6 7 8 ...
275
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
277
12. Threads in Java
Synchronisation
Klassen- und Instanzmethoden mit synchronized
12. Threads in Java
Synchronisation
Solch ein Puffer wird von einem Erzeuger-Thread mit den Werten von 0 bis 4 gefüllt.
class Erzeuger extends Thread {
Wert w;
public Erzeuger(Wert w) { this.w = w; }
• Eine mit synchronized gekennzeichnete Klassenmethode kann höchstens von
einem Thread gleichzeitig ausgeführt werden.
• Eine mit synchronized gekennzeichnete Instanzmethode kann prinzipiell von
mehreren Threads gleichzeitig ausgeführt werden.
• Aber für jedes Objekt kann es maximal einen Thread geben, der sich in einer mit
synchronized gekennzeichneten Instanzmethode befindet.
• Bei Instanzmethoden ist die Synchronisation also objektbezogen.
public void run() {
for (int i=0 ; i<5 ; i++) {
w.put(i);
System.out.println("Erzeuger
put: " + i);
try {
sleep((int) (Math.random() * 100));
}
catch(InterruptedException e) { }
}
}
}
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
12. Threads in Java
278
Synchronisation
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
12. Threads in Java
280
Synchronisation
Ein Verbraucher-Thread holt die Werte aus dem Puffer.
Das Erzeuger/Verbraucher-Problem
class Verbraucher extends Thread {
Wert w;
Wir betrachten einen (zunächst abstrakten) einfachen Puffer, der maximal einen
Integer-Wert aufnehmen kann.
public Verbraucher(Wert w) { this.w = w; }
public void run() {
int v;
for (int i=0 ; i<5 ; i++) {
v = w.get();
System.out.println("Verbraucher get: " + v);
try {
sleep((int) (Math.random() * 100));
}
catch(InterruptedException e) { }
}
}
abstract class Wert {
protected int wert;
abstract public int get();
abstract public void put(int w);
}
}
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
279
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
281
12. Threads in Java
Synchronisation
Wir spezialisieren die Pufferklasse Wert zunächst auf einfache Weise:
Die Klasse Object stellt zur Verfügung:
• public final void wait()
der Thread, der gerade ausgeführt wird, wird suspendiert, bis ein anderer Thread
notify oder notifyAll für das aktuelle Objekt ausführt.
• public final void notify()
reaktiviert einen einzelnen Thread, der sich im Wartezustand bezüglich des aktuellen Objekts befindet.
• public final void notifyAll()
reaktiviert alle Thread, die sich im Wartezustand bezüglich des aktuellen Objekts
befinden.
Unser Testprogramm
public class EVTest1 {
public static void main(String[] args) {
Wert
w = new SchlechterWert();
Erzeuger
e = new Erzeuger(w);
Verbraucher w = new Verbraucher(w);
e.start();
v.start();
}
}
12. Threads in Java
Möglicher Ablauf:
Erzeuger
put:
Verbraucher get:
Erzeuger
put:
Verbraucher get:
Verbraucher get:
Verbraucher get:
Erzeuger
put:
Erzeuger
put:
Erzeuger
put:
Verbraucher get:
Synchronisation
Object-Methoden zur Steuerung von Threads
class SchlechterWert extends Wert {
public synchronized int get() { return wert; }
public synchronized void put(int w) { wert = w; }
}
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
12. Threads in Java
282
Synchronisation
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
12. Threads in Java
284
Synchronisation
Eine bessere Spezialisierung des abstrakten Puffers:
0
0
1
1
1
1
2
3
4
4
• Der Erzeuger produziert zwar die Werte 0 bis
4,
• der Verbraucher entnimmt dem Puffer aber einige Werte mehrfach bzw. einige überhaupt
nicht.
• Wir müssen dafür sorgen, dass der Verbraucher immer nur dann aktiv wird, wenn der
Erzeuger wieder einen neuen Wert bereitgestellt hat.
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
283
class GuterWert extends Wert {
private boolean verfuegbar = false;
public synchronized int get() {
if (!verfuegbar)
try {
wait();
}
catch (InterruptedException ie) { }
verfuegbar = false;
notify();
return wert;
}
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
285
12. Threads in Java
Synchronisation
12. Threads in Java
Synchronisation
Zusammenfassung
public synchronized int put(int w) {
if (verfuegbar)
try {
wait();
}
catch (InterruptedException ie) { }
wert = w;
verfuegbar = true;
notify();
}
•
•
•
•
Threads: parallele Kontrollflüsse in einem Programm
Implementierung: implements Runnable oder extends Thread
kritische Bereiche, Monitor: synchronized
Gegenseitiges Warten: wait(), notify()
}
• Das Flag verfuegbar zeigt an, ob ein Wert bereitsteht.
• Kein Wert verfügbar, dann wait() in get().
• put() weckt mit notify() den Verbraucher-Thread, wenn Wert zur Verfügung
steht.
• put()/get() analog
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
12. Threads in Java
286
Synchronisation
Im Testprogramm ändern wir die Zeile zur Erzeugung des Puffers:
Wert w = new GuterWert();
Damit erhalten wir dann eine saubere Verarbeitung:
Erzeuger
Verbraucher
Erzeuger
Verbraucher
Erzeuger
Verbraucher
Erzeuger
Verbraucher
Erzeuger
Verbraucher
put:
get:
put:
get:
put:
get:
put:
get:
put:
get:
0
0
1
1
2
2
3
3
4
4
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
287
Peter Becker, Programiersprache Java — FH Bonn-Rhein-Sieg, SS 08
288
Herunterladen