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