Nebenläufigkeit in Java Prof. Dr. Margarita Esponda Prof. Dr. Margarita Esponda 1
Gliederung der Vorlesung - Konzepte der Nebenläufigkeit - Threads in Java - Synchronisationsprobleme - Klassische Lösungen - Semaphoren - Monitore - Lebenszyklus eines Threads Prof. Dr. Margarita Esponda 2
Wo finden wir Nebenläufigkeit? Wettersimulation Betriebssysteme Suchmaschinen GUIs Computerspiele Prof. Dr. Margarita Esponda 3
Parallelität Echte Parallelität Virtuelle Parallelität Prozesse laufen auf unterschiedlichen CPUs Mehrere Prozesse laufen auf der selben CPU Prozess 1 Prozess 1 Prozess 2 Prozess 2 Prozess 3 Prozess 3 Prozess 3 Prozess 3 Prof. Dr. Margarita Esponda 4
Grundlegende Begriffe "single program" In der Steinzeit der PCs durfte man nur einzelne Programme streng sequentiell ausführen. "task switching" Mehrere Programme liegen im Hauptspeicher. Durch Umschaltung setzt man die Ausführung eines der Programme fort. "multitasking" Das Betriebssystem schaltet die Programme um. Man hat das Gefühl, dass mehrere Programme parallel laufen. "multithreading" Es gibt mehrere Programmfäden, die parallel innerhalb eines Programms ausgeführt werden. Prof. Dr. Margarita Esponda 5
"Multitasking" vs. "Multithreading" mehrere Prozesse (process) jeder Prozess hat eine eigene Umgebung Kein gemeinsamer Adressraum Verwaltung durch das Betriebssystem mehrere Programmfäden (thread) alle Threads laufen in der gleichen Umgebung gemeinsamer Adressraum!!! Verwaltung innerhalb eines Prozesses Prof. Dr. Margarita Esponda 6
Multitasking + Multithreading Prozess 1 Prozess 2 Prozess 3 Thread 1 Thread 2 Adressraum für Prozess 1 Adressraum für Prozess 2 Adressraum für Prozess 3 Prof. Dr. Margarita Esponda 7
Multitasking Betriebssystem Prozess 1 Prozess 2 Lokaler Speicher Lokaler Speicher Prozess 3 Lokaler Speicher Gemeinsamer Speicher Prozesse werden direkt vom Betriebssystem verwaltet. Jeder Prozess bekommt einen eigenen Speicherbereich vom Betriebssystem zugeteilt. Prof. Dr. Margarita Esponda 8
Multitasking + Multithreading Betriebssystem Prozess 1 Prozess 2 Lokaler Speicher Lokaler Speicher JVM Java Virtuelle Maschine Thread 1 Thread 2 Lokaler Speicher Lokaler Speicher Thread 3 Lokaler Speicher Gemeinsamer Speicherbereich Prof. Dr. Margarita Esponda 9
Gliederung der Vorlesung - Konzepte der Nebenläufigkeit - Threads in Java - Synchronisationsprobleme - Klassische Lösungen - Semaphoren - Monitore - Lebenszyklus eines Threads Prof. Dr. Margarita Esponda 10
Threads Ausführungsstrang Ein Thread ist ein eigenständiges Programmfragment, das parallel zu anderen Programmfragmenten (Threads) laufen kann. Threads: können die Bedienbarkeit von Dialoganwendungen verbessern, indem rechenintensive Anwendungen im Hintergrund ablaufen. Threads werden auch als leichtgewichtige Prozesse bezeichnet. Prof. Dr. Margarita Esponda 11
Threads in Java Ein Programmfaden oder Ausführungsstrang wird durch ein Objekt des Typs Thread repräsentiert. Die Klasse Thread ist eine vollständige Implementierung des Runnable-Interfaces. Das Runnable-Interface stellt Klassen von Objekten die parallel ausführbaren Programmcode beinhalten. public interface Runnable { public abstract void run(); Prof. Dr. Margarita Esponda 12
Programmierung von Threads Thread-Klassen können in Java auf zwei verschiedene Arten definiert werden: als Unterklasse der Thread-Klasse public class Counter extends Thread {... public void run() {... Die Methode run() muss überschrieben werden durch Implementierung der Runnable-Schnittstelle public class Counter implements Runnable {... public void run() {... Die Methode run() muss implementiert werden Prof. Dr. Margarita Esponda 13
Die Thread-Klasse Thread ( ); // erzeugt ein leeres Thread-Objekt mit // automatisch festgelegtem Namen Thread-N Thread ( String name ); // erzeugt ein leeres Thread-Objekt mit Namenname Thread ( Runnable target ) Einige Konstruktoren // erzeugt zu Runnable-Objektziel ein Thread-Objekt Thread ( Runnable target, String name ) // erzeugt zu Runnable-Objektziel ein Thread-Objekt // mit Namen name Prof. Dr. Margarita Esponda 14
Die Thread-Klasse run()-methode Innerhalb der run()-methode befinden sich die potenziell parallel ausführbaren Aktivitäten eines Threads. start() - Methode Startet ein Thread und zwar: - es wird Speicher für den Thread bereit gestellt - und die run-methode wird aufgerufen Prof. Dr. Margarita Esponda 15
Beispiel: public class SimpleThread extends Thread { public SimpleThread ( String name ) { super(name); public void run(){ for( int i=0; i<100; i++ ) System.out.print( this.getname() ); public class TestSimpleThread { public static void main( String[] args ){ SimpleThread tanne = new SimpleThread("Anne "); SimpleThread tpeter = new SimpleThread("Peter "); tanne.start(); tpeter.start(); Anne Anne Anne Anne Anne Anne Anne Anne Anne Anne Anne Peter Peter Peter Peter Peter Peter Peter Peter Peter Peter Peter Peter Peter Peter Peter Peter Anne Anne Anne Anne Anne Anne Anne Anne Anne Anne Anne Anne Anne Anne Anne Peter Peter Peter Peter Peter Peter........... Prof. Dr. Margarita Esponda 16
Java-Programm mit Multithreading main... thread1 = new Thread() thread2 = new Thread() thread1.start() thread2.start() maindosomething() mainwork() mainwork() end() thread1.. void run(){ work1() work2() work3() work4() work5() thread2.. void run(){ work1() work2() work3() work4() work5() work6() Zeit Prof. Dr. Margarita Esponda 17
weitere wichtige Methoden join() - Methode Um auf die Beendigung eines oder mehrerer Threads zu warten, benutzt man die join() - Methode. sleep(int millis) - Methode Ein Thread kann mit der sleep()-methode für eine vorbestimmte Zeit in den Zustand "not runnable" versetzt werden. Prof. Dr. Margarita Esponda 18
Implementierung als Unterklasse von Thread public class SimpleThread extends Thread { int lifetime; public SimpleThread( String name, int lifetime ) { super( name ); this.lifetime = lifetime; public void run() { for ( int i=0; i<lifetime; i++ ) { try { this.sleep( 1000 ); System.out.println( "Ich bin "+this.getname() ); catch ( InterruptedException ie ) System.out.println(ie); Prof. Dr. Margarita Esponda 19
public class TestSimpleThread { public static void main( String[] args ) { SimpleThread t1 = new SimpleThread( "Andreas", 5 ); SimpleThread t2 = new SimpleThread( "Hanna", 5 ); SimpleThread t3 = new SimpleThread( "Anne", 3 ); t1.start(); t2.start(); t3.start(); System.out.println( "Ich bin main" ); Ausgabe: Ich bin main Ich bin Andreas Ich bin Hanna Ich bin Anne Ich bin Andreas Ich bin Hanna Ich bin Anne Ich bin Andreas Ich bin Hanna Ich bin Anne Ich bin Andreas Ich bin Hanna Ich bin Andreas Ich bin Hanna main t1 t2 t3 t1 t2 t3 t1 t2 t3 t1 t2 t1 t2 Prof. Dr. Margarita Esponda 20
public class TestSimpleThread { public static void main( String[] args ) { SimpleThread t1 = new SimpleThread( "Andreas", 5 ); SimpleThread t2 = new SimpleThread( "Hanna", 5 ); t1.start(); t2.start(); try { Thread-Beispiel t1.join(); // Warte auf den 1.Thread t2.join(); // Warte auf den 2.Thread catch ( InterruptedException ie ) { System.out.println( ie ); System.out.println( "Ich bin main" ); // end of main // end of class Ich bin Andreas Ich bin Hanna Ich bin Andreas Ich bin Hanna Ich bin Andreas Ich bin Hanna Ich bin Andreas Ich bin Hanna Ich bin Andreas Ich bin Hanna Ich bin main t1 t2 t1 t2 t1 t2 t1 t2 t1 t2 main Prof. Dr. Margarita Esponda 21
Implementierung der Runnable-Schnittstelle public class SimpleRunnable implements Runnable { // Instanzvariablen int lifetime; Thread thread; public SimpleRunnable( String name, int lifetime ) { this.thread = new Thread( this, name ); this.lifetime = lifetime; public void run() { for ( int i=0; i<this.lifetime; i++ ) { System.out.println( "Ich bin "+this.thread.getname() ); // end of run // end of class Prof. Dr. Margarita Esponda 22
Implementierung der Runnable-Schnittstelle public class TestSimpleRunnable { public static void main( String[] args ) { SimpleRunnable t1 = new SimpleRunnable( "Peter", 6 ); SimpleRunnable t2 = new SimpleRunnable( "Hanna", 6 ); Thread t1.thread.start(); t2.thread.start(); System.out.println( " Ich bin am Ende " ); // end of main // end of class Ausgabe: Ich bin am Ende Ich bin Peter Ich bin Hanna Ich bin Peter Ich bin Hanna Ich bin Peter Ich bin Hanna Ich bin Peter Ich bin Hanna Ich bin Peter Ich bin Hanna Ich bin Peter Ich bin Hanna main t1 t2 t1 t2 t1 t2 t1 t2 t1 t2 t1 t2 Prof. Dr. Margarita Esponda 23
public class TestSimpleRunnable { join()-methode public static void main( String[] args ) { SimpleRunnable t1 = new SimpleRunnable( "Peter", 6 ); SimpleRunnable t2 = new SimpleRunnable( "Hanna", 6 ); Ausgabe: Thread t1.thread.start(); t2.thread.start(); try { t1.thread.join(); t2.thread.join(); catch ( InterruptedException ie ) { System.out.println( ie ); System.out.println( " Ich bin am Ende " ); // end of main // end of class Ich bin Peter Ich bin Hanna Ich bin Peter Ich bin Hanna Ich bin Peter Ich bin Hanna Ich bin Peter Ich bin Hanna Ich bin Peter Ich bin Hanna Ich bin Peter Ich bin Hanna Ich bin am Ende t1 t2 t1 t2 t1 t2 t1 t2 t1 t2 t1 T2 main Prof. Dr. Margarita Esponda 24
Weitere Klassenmethoden der "Thread"-Klasse public class Thread implements Runnable { /* Methoden */... static int activecount() static Thread currentthread() static boolean holdslock(object obj) static boolean interrupted() static void setdefaultuncaughtexceptionhandler... ( Thread.UncaughtExceptionHandler eh ) Prof. Dr. Margarita Esponda 25
weitere Instanzmethoden der "Thread"-Klasse public class Thread implements Runnable { /* Methoden */... int getpriority() String getname() long getid() boolean isalive( ) void setdaemon(boolean on) void setname(string name) void setpriority(int newpriority) StackTraceElement[] getstacktrace() Thread.State getstate()... Prof. Dr. Margarita Esponda 26
Es gibt kein Java-Programm ohne Threads. Betriebssystem Betriebssystem Java-Anwendung garbage collector Browser garbage collector main-thread Java-Applet Java-Applet GUI setvisible(true) init-thread init-thread event-dispatcher Prof. Dr. Margarita Esponda 27
Gliederung der Vorlesung - Konzepte der Nebenläufigkeit - Threads in Java - Synchronisationsprobleme - Klassische Lösungen - Semaphoren - Monitore - Lebenszyklus eines Threads Prof. Dr. Margarita Esponda 28
Was ist das Problem mit Threads? - es ist sehr einfach Threads in Java zu programmieren - es ist schwer Threads ohne Fehler zu programmieren! Was geschieht, wenn Threads gemeinsame Variablen oder Ressourcen benutzen? Prof. Dr. Margarita Esponda 29
Threads mit gemeinsamen Daten T1 T2 public void run(){.. x = x + 1; y = y + 1; paint();. public void run(){.. x = x * 2; y = y * 2; paint();. x=y T1: x = x + 1; x = x * 2; T2: y = y * 2; T2: paint(); T2: T1: y = y + 1; 3, 3 4, 3 8, 3 8, 6 8, 6 8, 7 x=y? Prof. Dr. Margarita Esponda 30
Synchronisationsprobleme public class Asynchron extends Thread { public static int zaehler = 0; Klassenvariable Gemeinsame Variable für alle Objekte der Klasse Asynchron. public void run() { while ( zaehler<25 ) { zaehler++; System.out.print( this + " " ); System.out.println( zaehler ); public static void main( String args[] ) { Thread t1 = new Asynchron(); Thread t2 = new Asynchron(); t1.start(); t2.start(); Prof. Dr. Margarita Esponda 31
Synchronisationsprobleme zaehler 0 t1 public void run() { while ( zaehler<25 ) { zaehler++; System.out.print( this + " " ); System.out.println( zaehler ); t2 public void run() { while ( zaehler<25 ) { zaehler++; System.out.print( this + " " ); System.out.println( zaehler ); Prof. Dr. Margarita Esponda 32
Synchronisationsprobleme zaehler 678 t0 public void run() { while ( zaehler<25 ) { zaehler++; System.out.print( this + " " ); System.out.println( zaehler ); t1 public void run() { while ( zaehler<25 ) { zaehler++; System.out.print( this + " " ); System.out.println( zaehler ); Prof. Dr. Margarita Esponda 33
Synchronisationsprobleme Thread[Thread-0,5,main] 1 Thread[Thread-0,5,main] 2 Thread[Thread-0,5,main] 3 Thread[Thread-0,5,main] 4 Thread[Thread-0,5,main] 5 Thread[Thread-0,5,main] 6 Thread[Thread-1,5,main] Thread[Thread-0,5,main] 8 8 Thread[Thread-0,5,main] 9 Thread[Thread-0,5,main] 10 Thread[Thread-0,5,main] 11 Thread[Thread-0,5,main] 12 Thread[Thread-0,5,main] 13 Thread[Thread-0,5,main] 14 Thread[Thread-0,5,main] 15 Thread[Thread-0,5,main] 16 Thread[Thread-0,5,main] 17 Thread[Thread-0,5,main] 18 Thread[Thread-0,5,main] Thread[Thread-1,5,main] 20 20 Thread[Thread-0,5,main] Thread[Thread-1,5,main] 22 22 Thread[Thread-0,5,main] Thread[Thread-1,5,main] 24 Thread[Thread-0,5,main] 25 Prof. Dr. Margarita Esponda 34
Synchronisationsprobleme zaehler 24 t0 public void run() { while ( zaehler<25 ) { zaehler++; System.out.print( this + " " ); System.out.println( zaehler ); t1 public void run() { while ( zaehler<25 ) { zaehler++; System.out.print( this + " " ); System.out.println( zaehler ); Prof. Dr. Margarita Esponda 35
Synchronisationsprobleme zaehler 24 t0 public void run() { while ( zaehler<25 ) { zaehler++; System.out.print( this + " " ); System.out.println( zaehler ); t1 public void run() { while ( zaehler<25 ) { zaehler++; System.out.print( this + " " ); System.out.println( zaehler ); Prof. Dr. Margarita Esponda 36
Synchronisationsprobleme zaehler 24 t0 public void run() { while ( zaehler<25 ) { zaehler++; System.out.print( this + " " ); System.out.println( zaehler ); t1 public void run() { while ( zaehler<25 ) { zaehler++; System.out.print( this + " " ); System.out.println( zaehler ); Prof. Dr. Margarita Esponda 37
Synchronisationsprobleme zaehler 24 t0 public void run() { while ( zaehler<25 ) { zaehler++; System.out.print( this + " " ); System.out.println( zaehler ); t1 public void run() { while ( zaehler<25 ) { zaehler++; System.out.print( this + " " ); System.out.println( zaehler ); Prof. Dr. Margarita Esponda 38
Synchronisationsprobleme zaehler 24 25 26 t0 public void run() { while ( zaehler<25 ) { zaehler++; System.out.print( this + " " ); System.out.println( zaehler ); t1 public void run() { while ( zaehler<25 ) { zaehler++; System.out.print( this + " " ); System.out.println( zaehler ); Prof. Dr. Margarita Esponda 39
Lösung: Wechselseitiger Ausschluss muss gewährleistet werden. T1 T2 public void run(){.. x = x + 1; y = y + 1; paint();. Kritischer Abschnitt public void run(){.. x = x * 2; y = y * 2; paint();. Kritischer Abschnitt nur ein Thread im kritischen Abschnitt Prof. Dr. Margarita Esponda 40
Kritische Abschnitte Wechselseitiger Ausschluss muss gewährleistet werden, wenn gemeinsame Ressourcen benutzt werden. kritischer Abschnitt Prof. Dr. Margarita Esponda 41
Typisches Beispiel: Drucker-Spooler simultaneous peripheral operations on line 0 1 2 3 4 QTail Erster freier Platz der Warteschlange Thread A... adr = liest QTail speichert Datei in adr inkrementiert QTail... Thread B... adr = liest QTail speichert Datei in adr inkrementiert QTail... Prof. Dr. Margarita Esponda 42
Klassisches Beispiel in der Literatur: Drucker-Spooler simultaneous peripheral operations on line 0 1 2 3 4 5 QTail =4 Unterbrechung Thread A... adr = liest QTail speichert Datei in adr inkrementiert QTail... Thread B... adr = liest QTail speichert Datei in adr inkrementiert QTail... Unterbrechung Prof. Dr. Margarita Esponda 43
Typisches Beispiel: Drucker-Spooler simultaneous peripheral operations on line 0 1 2 3 4 5 6 Fehler! QTail Thread A Thread B...... Unterbrechung adr = liest QTail speichert Datei in adr inkrementiert QTail... adr = liest QTail speichert Datei in adr inkrementiert QTail... Kritischer Abschnitt Unterbrechung Prof. Dr. Margarita Esponda 44
Gliederung der Vorlesung - Konzepte der Nebenläufigkeit - Threads in Java - Synchronisationsprobleme - Klassische Lösungen - Semaphoren - Monitore - Lebenszyklus eines Threads Prof. Dr. Margarita Esponda 45
Kritische Abschnitte Kritische Abschnitte sind die Teile des Programms, in denen auf gemeinsam benutzten Speicher bzw. Ressourcen zugegriffen werden kann. Wechselseitiger Ausschluss muss gewährleistet werden. Wie? 1. Unterbrechungen ausschalten 2. Variablen sperren 3. Strikter Wechsel 4. Petersons Lösung 5. Die TSL- Anweisung Prof. Dr. Margarita Esponda 46
1. Unterbrechungen ausschalten disable_interrupts(); kritischer_abschnitt(); enable_interrupts(); Jedoch: Benutzerprozesse sollten nicht die Macht haben, alle Unterbrechungen auszuschalten. Die Ausschaltung von Unterbrechungen ist ein Privileg des Betriebssystems. Nicht geeignet als Ausschlussmechanismus für Benutzerprozesse. Prof. Dr. Margarita Esponda 47
2. Variablen sperren Nehmen wir an, zwei Threads möchten eine Warteschlange gemeinsam benutzen. Warteschlange Sperre = 01 Wenn Sperre = 0, ist die Warteschlange gesperrt Wenn Sperre = 1, ist die Warteschlange nicht gesperrt Die Prozesse benutzen wiederum eine gemeinsame Variable, um den Zugriff auf die Warteschlange zu synchronisieren. Prof. Dr. Margarita Esponda 48
2. Variablen sperren Warteschlange Sperre = 01 Thread A Thread B if (Sperre == 0) Interrupt if (Sperre == 0) Sperre = 1; Sperre = 1; kritischer kritischer Abschnitt wird ausgeführt else warten... Beide Prozesse können in ihren kritischen Bereich gleichzeitig eintreten. Abschnitt wird ausgeführt else warten... Interrupt Prof. Dr. Margarita Esponda 49
3. Strikter Wechsel Warteschlange turn = 01 while ( true ) { while ( turn!= 0 ); critical_region(); turn = 1; noncritical_region(); while ( true ) { while ( turn!= 1 ); critical_region(); turn = 0; noncritical_region(); Thread 0 Thread 1 Prof. Dr. Margarita Esponda 50
3. Strikter Wechsel Warteschlange turn = 01 while ( true ) { while ( turn!= 0 ); critical_region(); turn = 1; noncritical_region(); while ( true ) { while ( turn!= 1 ); critical_region(); turn = 0; noncritical_region(); Thread 0 Thread 1 Prof. Dr. Margarita Esponda 51
3. Strikter Wechsel Warteschlange turn = 1 Erst wenn Thread 1 wieder in seinem kritischen Abschnitt gewesen ist, darf Thread 0 seinen kritischen Abschnitt wieder betreten. Leider erfordert diese Lösung einen strikten Wechsel zwischen den Prozessen. while ( true ) { while ( turn!= 0 ); critical_region(); turn = 1; noncritical_region(); while ( true ) { while ( turn!= 1 ); critical_region(); turn = 0; noncritical_region(); Thread 0 Thread 1 Prof. Dr. Margarita Esponda 52
4. Petersons Lösung für zwei Prozesse public class PetersonThread extends Thread { static int turn = 0; static boolean[] ready = { false, false ; int id, other; public PetersonThread( int id ){ this.id = id; this.other = 1-id; public void run() { while(true){ ready[id] = true; turn = other; while( ready[other] && turn==other ); critical_region(); ready[id]=false; noncritical_region(); Prof. Dr. Margarita Esponda 53
4. Petersons Lösung für zwei Prozesse 0 1 ready false false turn = 0 public void run() { while ( true ) { ready[0] = true ; turn = 1; while(ready[1] && turn==1); { //kritischer Abschnitt ready[0] = false; { //Rest des Programms public void run() { while ( true ) { ready[1] = true ; turn = 0; while(ready[0] && turn==0); { //kritischer Abschnitt ready[1] = false; { //Rest des Programms Thread 0 Thread 1 Prof. Dr. Margarita Esponda 54
5. TSL-Anweisung Lösung mit Hardware-Unterstützung. Insbesondere Mehrprozessorrechner haben TSL- Anweisungen. TSL RX, LOCK Test and Set Lock Wenn die CPU eine TSL-Anweisung im Speicher ausführt, wird der Speicherbus gesperrt, bis er fertig ist. Prof. Dr. Margarita Esponda 55
Peterson + TSL-Anweisung Prioritätsumkehrproblem flag 0 1 false false turn = 0 P 0 hat eine höhere Priorität while ( true ) { flag[0] = true ; turn = 1; while(flag[1] && turn==1); { //kritischer Abschnitt flag[0] = false; { //Rest des Programms P 1 hat eine niedrigere Priorität while ( true ) { flag[1] = true ; turn = 0; while(flag[0] && turn==0); { //kritischer Abschnitt flag[1] = false; { //Rest des Programms Prof. Dr. Margarita Esponda 56
Peterson + TSL-Anweisung Prioritätsumkehrproblem flag 0 1 true true turn = 1 P 0 hat eine höhere Priorität while ( true ) { flag[0] = true ; turn = 1; while(flag[1] && turn==1); { //kritischer Abschnitt flag[0] = false; { //Rest des Programms P 1 hat eine niedrigere Priorität while ( true ) { flag[1] = true ; turn = 0; while(flag[0] && turn==0); { //kritischer Abschnitt flag[1] = false; { //Rest des Programms Prof. Dr. Margarita Esponda 57
Peterson + TSL-Anweisung Der Peterson-Algorithmus und die TSL-Lösung haben den Nachteil, dass aktives Warten erforderlich ist. Probleme 1. CPU-Zeit Verschwendung 2. Prioritätsumkehrproblem Prioritätsumkehrproblem Obwohl P 0 eine höhere Priorität als P 1 hat, muss er immer länger als P 1 warten, um in seinen kritischen Abschnitt eintreten zu können. Prof. Dr. Margarita Esponda 58
Schlafen und Aufwecken Sleep und Wakeup Eine Alternative zum aktiven Warten ist: Schlafen gehen, anstatt CPU-Zeit zu verschwenden. Aber ein schlafender Prozess kann sich selber nicht aufwecken. D.h. er braucht einen entsprechenden Partner, der ihn wieder aufweckt. Prof. Dr. Margarita Esponda 59
Gliederung der Vorlesung - Konzepte der Nebenläufigkeit - Threads in Java - Synchronisationsprobleme - Klassische Lösungen - Semaphoren - Monitore - Lebenszyklus eines Threads Prof. Dr. Margarita Esponda 60
Semaphoren Semaphoren sind eine der wichtigsten Datenstrukturen, die für die Synchronisation von Prozessen in fast allen Betriebssystemen zur Verfügung gestellt werden. Semaphoren als eine Lösung für Prozesssynchronisation wurde von Edsger W. Dijkstra 1965 konzipiert. Semaphoren haben folgende Komponenten: - ein Zähler - eine acquire-operation - eine release-operation Prof. Dr. Margarita Esponda 61
Semaphoren Der Inhalt der Semaphoren wird nur mit Hilfe der Operationen acquire und release verändert. Die aquire- und release-operationen sind atomare Operationen, die einen Thread eventuell wecken oder zum Schlafen schicken. Prof. Dr. Margarita Esponda 62
Lösung mit Semaphoren Ab Java 1.5 ist eine Semaphor-Klasse in dem Paket java.util.concurrent enthalten. Allgemeine Verwendung eines einfachen binären Semaphors oder mutex. Semaphore sem = new Semaphore(1);... public void run(){... sem.acquire(); kritische_abschnitt(); sem.release();...... Prof. Dr. Margarita Esponda 63
Probleme mit Semaphoren Folgende einfache Fehler werden oft vom Programmierer gemacht: s.release();... // Kritischer Abschnitt... s.acquire(); s.acquire();... // Kritischer Abschnitt... s.acquire(); Mehrere Threads können in den kritischen Abschnitt eintreten. Ein Deadlock findet innerhalb des Threads statt. Prof. Dr. Margarita Esponda 64
Probleme mit Semaphoren Folgende Fehler sind im Programm schwer zu erkennen, weil die Probleme nur bei bestimmten Ausführungssequenzen auftreten. T 0 T 1 T0: S1.acquire(); S1.acquire(); S2.acquire(); T1: S2.acquire(); S2.acquire(); S1.acquire(); T0: S2.acquire(); Wartet!.... T1: S1.acquire(); Wartet!.. S2.release(); S1.release(); S1.release(); S2.release(); Verklemmung! Prof. Dr. Margarita Esponda 65
Deadlocks Ein Deadlock oder Verklemmung ist ein Zustand, bei dem mindestens zwei Prozesse auf Ressourcen warten, die einem anderen beteiligten Prozess zugeteilt sind und nicht mehr frei gegeben werden können. Klassisches Beispiel: Prof. Dr. Margarita Esponda 66
Gliederung der Vorlesung - Konzepte der Nebenläufigkeit - Threads in Java - Synchronisationsprobleme - Klassische Lösungen - Semaphoren - Monitore - Lebenszyklus eines Threads Prof. Dr. Margarita Esponda 67
Monitore Sind abstrakte Datentypen (ADT) Gemeinsame Daten T a T b T c T d Nur durch die Operationen des Monitors dürfen die inneren Daten geändert werden. Ein Monitor garantiert gegenseitigen Ausschluss des Zugriffs auf die Daten innerhalb des Monitors.... Operationen Nur ein Thread darf zu einem bestimmten Zeitpunkt innerhalb des Monitors aktiv sein. Prof. Dr. Margarita Esponda 68
Monitore Nur ein Thread darf zu einem bestimmten Zeitpunkt innerhalb des Monitors aktiv sein. Das Objekt ist blockiert T 3 T 2 T 4 T 8 T 1 T o Besitzer des Objektes Monitore können in Java als Klassen mit synchronized- Methoden und geeigneter Verwendung der wait-, notify- und notifyall-methoden realisiert werden. Prof. Dr. Margarita Esponda 69
Das synchronized-schlüsselwort in Java... synchronized (Objektvariable) {...... In Java ist jede Instanz der Klasse java.lang.object mit einer Sperrvariable verknüpft. Diese Sperre entspricht einem booleschen Semaphor. Beispiel:... synchronized ( nums ) { for ( int i=0; i<nums.length; i++ ) { if (nums[i] < 0) nums[i] = -nums[i];... Prof. Dr. Margarita Esponda 70
Das synchronized-schlüsselwort in Java T1... synchronized (A) {... synchronized (B) {...... T2... synchronized (B) {... synchronized (A) {...... Verklemmung! Prof. Dr. Margarita Esponda 71
Monitore Gemeinsame Daten T a T b T c T d Bedingungsvariablen b 1 T 2 T 1 T 3 Warteschlangen, die mit b 2 b 3 T 7 T 4 T 5 Bedingungsvariablen verbunden sind. Sprachen mit Monitorkonzept... Concurrent Pascal C# Erlang Ada Squeak Smalltalk Java Prof. Dr. Margarita Esponda 72
Wait und Notify Die wait() und notify()-methoden können nur innerhalb eines synchronized-blockes stehen. Aufruf der wait-methode - Der Thread gibt die Objektschlüssel zurück. - Der Thread selber geht in den blockierten Zustand. - Der blockierte Thread wird in die Warteschlange des Objektes gestellt. Aufruf der notify-methode - Ein beliebiger Thread aus der Warteschlange des Objektes wird gewählt. - Der gewählte Thread wird in den Entry-Set gestellt. - Der Zustand des Threads wird auf runnable gesetzt. Prof. Dr. Margarita Esponda 73
Monitore in Java T 8 T 1 Entry-Set T 8 T 1 Das Objekt ist blockiert wait T 3 T 2 T 4 T o Wait-Set Hier warten die Threads auf den Lock des Objektes Besitzer des Objektes Hier warten die Threads auf ein Notify Prof. Dr. Margarita Esponda 74
Monitore in Java T 8 T 1 Entry-Set T 8 T 1 Das Objekt ist blockiert notify T o Wait-Set T 3 T 2 T 4 Hier warten die Threads auf den Lock des Objektes Besitzer des Objektes Hier warten die Threads auf ein Notify Prof. Dr. Margarita Esponda 75
Leser/Schreiber-Problem - beliebig viele Threads dürfen gleichzeitig lesen - nur ein Thread darf schreiben (keiner darf lesen) public class Database implements ReadWriteLock { private int readercounter; private boolean Writing; public Database(){ readercounter = 0; Writing = false;... //end of class Database Prof. Dr. Margarita Esponda 76
Leser/Schreiber-Problem public class Database implements ReadWriteLock {... public synchronized void acquirereadlock() { while( Writing == true ){ try { wait(); catch (InterruptedException e) {... ++readercounter; public synchronized void acquirewritelock() { while( readercounter>0 Writing==true ){ try { wait(); catch (InterruptedException e) {... Writing = true; //end of class Database Prof. Dr. Margarita Esponda 77
Leser/Schreiber-Problem public class Database implements ReadWriteLock {... public synchronized void releasereadlock() { --readercounter; /* der letzte Leser meldet sich */ if ( readercounter==0 ) notify(); public synchronized void releasewritelock() { Writing = false; notifyall();... //end of class Database Verhungern! Prof. Dr. Margarita Esponda 78
Gliederung der Vorlesung - Konzepte der Nebenläufigkeit - Threads in Java - Synchronisationsprobleme - Klassische Lösungen - Semaphoren - Monitore - Lebenszyklus eines Threads Prof. Dr. Margarita Esponda 79
Lebens-Zyklus eines Threads JDK 1.1 new Thread() suspend() New start() rechen- bereit resume() blockiert stop() stop() stop() Tot Deadlock-Probleme! Prof. Dr. Margarita Esponda 80
Verklemmungsprobleme stop kritischer Abschnitt Verklemmung! Prof. Dr. Margarita Esponda 81
Lebens-Zyklus eines Threads JDK 1.2 new Thread() schlafend IO- blockiert interrupt Time out IO-End sleep() IO-Operation neu start() rechen- bereit yield() Scheduling rechnend terminiert tot interrupt notify() wait() Freigegeben Monitor besetzt interrupt wartend Monitor- blockiert Prof. Dr. Margarita Esponda 82
java.util.concurrent - Klassen mit unteilbarem (atomarem) Zugriff AtomicBoolean, AtomicInteger, AtomicIntegerArray, AtomicLong, AtomicReference, usw. - Erweiterte Sammlungsklassen für Threads ConcurrentLinkedQueue, ConcurrentHashMap, usw. - Semaphor-Klasse - Klassen für ThreadPools - Erweiterte Klassen für Sperren und Synchronisation Prof. Dr. Margarita Esponda 83
Threads Unterbrechungen in Java Threads zu unterbrechen ist kompliziert, wenn diese gerade gemeinsame Ressourcen verwenden. In Java haben Threads einen Unterbrechungsanforderungszustand. Asynchrone Unterbrechung stop() Deprecated Deferred Cancellation (verzögerte Unterbrechung) Unterbrechungen interrupt() Der interruption-status des Threads wird gesetzt. interrupted() Der interruption-status des Threads wird geprüft und zurück gesetzt. isinterrupted() Der interruption-status des Threads wird nur geprüft. Prof. Dr. Margarita Esponda 84
Threads unterbrechen in Java Die interrupt- Methode setzt nur den Unterbrechungsanforderungszustand eines Threads auf true. Um einen Thread tatsächlich unterbrechen zu können, müssen wir unterbrechbare Threads programmieren, die diesen Zustand regelmäßig selber überprüfen und ihre run-methode dann selber beenden, wenn alle Ressourcen abgegeben worden sind. Die interrupted-methode ist eine statische Methode, die den Unterbrechungsanforderungszustand des aktuellen Threads nur einmal überprüft und zurück setzt. Die interrupt-methode liefert eine InterruptedException, wenn ein Thread vorher wegen einer wait- sleep- oder join-methode bereits unterbrochen worden ist. Ab Java 1.5 ist es auch möglich Threads zu unterbrechen, die auf Ein- Ausgabe-Operationen warten (siehe Package java.nio). Prof. Dr. Margarita Esponda 85
Threads unterbrechen in Java public class UnterbrechbareThread extends Thread { public void run() { while( true ){ System.out.println("Hi! Ich lebe noch!!!"); if ( Thread.currentThread().isInterrupted() ){ System.out.println("Ich bin aufgefordert zu unterbrechen!"); break; /* Hier kann der Thread Ressourcen abgeben und * seine Ausführung sauber beenden. */ // end of class UnterbrechbareThread Unterbrechbare Threads müssen selber ihren interrupted-status kontrollieren und ihre Ausführung selber unterbrechen. Prof. Dr. Margarita Esponda 86
Threads unterbrechen in Java public class Test_UnterbrechbareThread { public static void main(string args[]){ Thread th = new Thread(new UnterbrechbareThread()); th.start(); Schlaf.nickerchen(200); th.interrupt(); Der Unterbrechungsanforderungszustand des Threads wird auf true gesetzt. Prof. Dr. Margarita Esponda 87
Literatur Java Threads. 3th Edition. 2004 Scott Oaks & Henry Wong. Prof. Dr. Margarita Esponda 88