Nebenläufigkeit in Java print - inf.fu-berlin.de · Java-Applet Java-Applet main-Thread...

Post on 12-Oct-2019

36 views 0 download

Transcript of Nebenläufigkeit in Java print - inf.fu-berlin.de · Java-Applet Java-Applet main-Thread...

NebenlNebenlääufigkeit in Javaufigkeit in Java

Prof. Dr. Prof. Dr. Margarita EspondaMargarita Esponda

Gliederung der VorlesungGliederung der Vorlesung

- Konzepte der Nebenläufigkeit

- Threads in Java

- Synchronisationsprobleme

- Klassische Lösungen

- Semaphoren

- Monitore

- Lebenszyklus eines Threads

WettersimulationBetriebssysteme

Suchmaschinen

Computerspiele

Wo finden wir NebenlWo finden wir Nebenlääufigkeit?ufigkeit?

GUIs

ParallelitParallelitäätt

Echte Parallelität Virtuelle Parallelität

Mehrere Prozesse laufen auf der selben CPU

Prozesse laufen auf unterschiedlichen CPUs

Prozess1Prozess1

Prozess2Prozess2

Prozess3Prozess3

Prozess1Prozess1

Prozess2Prozess2

Prozess3Prozess3

Prozess3Prozess3 Prozess3

Prozess3

Grundlegende BegriffeGrundlegende Begriffe

"single program""single program"

"task switching"

"multitasking"

"multithreading"

In der Steinzeit der PCs durfte man nur einzelne Programme streng sequentiell ausführen.

Mehrere Programme liegen im Hauptspeicher.

Durch Umschaltung setzt man die Ausführung eines der Programme fort.

Das Betriebssystem schaltet die Programme um. Man hat das Gefühl, dass mehrere Programme parallel laufen.

Es gibt mehrere Programmfäden, die parallel

innerhalb eines Programms ausgeführt werden.

"Multitasking" vs. "Multithreading""Multitasking" vs. "Multithreading"

mehrere Prozesse mehrere Prozesse ((processprocess))

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

Multitasking + MultithreadingMultitasking + Multithreading

Prozess 1 Prozess 2 Prozess 3

Thread 1 Thread 2

Adressraumfür Prozess 1

Adressraumfür Prozess 2 Adressraum

für Prozess 3

MultitaskingMultitasking

Prozess1Prozess1

Betriebssystem

Prozess2Prozess2

Prozess3Prozess3

Lokaler SpeicherLokaler Speicher

Lokaler SpeicherLokaler Speicher

Lokaler SpeicherLokaler Speicher

Gemeinsamer Speicher

Gemeinsamer Speicher Prozesse werden direkt vom

Betriebssystem verwaltet.

Jeder Prozess bekommt einen eigenen Speicherbereich vom Betriebssystem zugeteilt.

Multitasking + MultithreadingMultitasking + Multithreading

JVMJVM

Java Virtuelle MaschineJava Virtuelle Maschine

Thread1Thread1 Lokaler SpeicherLokaler Speicher

Lokaler SpeicherLokaler SpeicherThread2Thread2

Lokaler SpeicherLokaler SpeicherThread3Thread3

Gemeinsamer Speicherbereich

Gemeinsamer Speicherbereich

Prozess1Prozess1

Betriebssystem

Prozess2Prozess2

Lokaler SpeicherLokaler Speicher

Lokaler SpeicherLokaler Speicher

Gliederung der VorlesungGliederung der Vorlesung

- Konzepte der Nebenläufigkeit

- Threads in JavaThreads in Java

- Synchronisationsprobleme

- Klassische Lösungen

- Semaphoren

- Monitore

- Lebenszyklus eines Threads

Ein Ein ThreadThread ist ein eigenstist ein eigenstäändiges Programmfragment, ndiges Programmfragment, das parallel zu anderen Programmfragmenten (das parallel zu anderen Programmfragmenten (ThreadsThreads) ) laufen kann.laufen kann.

Threads:Threads: kköönnen die Bedienbarkeit von nnen die Bedienbarkeit von Dialoganwendungen verbessern, indem rechenintensive Dialoganwendungen verbessern, indem rechenintensive Anwendungen im Hintergrund ablaufen.Anwendungen im Hintergrund ablaufen.

ThreadsThreads werden auch als werden auch als leichtgewichtigeleichtgewichtige ProzesseProzessebezeichnet.bezeichnet.

Threads Ausführungsstrang

Threads in JavaThreads 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();

}

Programmierung von ThreadsProgrammierung von Threads

Thread-Klassen können in Java auf zwei verschiedene

Arten definiert werden:

als Unterklasseder Thread-Klasse

durch Implementierungder Runnable-Schnittstelle

public class Counter extends Thread { . . .

public void run(){

. . .}

}

public class Counter implements Runnable{ . . .

public void run(){

. . .}

}

Die Methode run() muss überschriebenwerden

Die Methode run() muss implementiertwerden

DieDie ThreadThread--KlasseKlasse

Thread ( ); // erzeugt ein leeres Thread-Objekt mit

// automatisch festgelegtem Namen Thread-N

Thread ( String name );// erzeugt ein leeres Thread-Objekt mit Namen name

Thread ( Runnable target )

// erzeugt zu Runnable-Objektziel ein Thread-Objekt

Thread ( Runnable target, String name )

// erzeugt zu Runnable-Objektziel ein Thread-Objekt

// mit Namen name

Einige Konstruktoren

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

DieDie ThreadThread--KlasseKlasse

Beispiel: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 AnnePeter 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 . . . . . . . . . . .

JavaJava--Programm mit MultithreadingProgramm mit Multithreading

Zeit

main .....thread1 = new Thread()

thread2 = new Thread()thread1.start()

void run(){work1()work2()work3()

work4()work5()

}

thread2.start()

mainDoSomething()

mainWork()mainWork()end()

}

.

..

.

void run(){work1()

work2()work3()work4()

work5()work6()

}

thread1

thread2

join() - Methode

sleep(int millis) - Methode

Um auf die Beendigung eines oder mehrerer Threads

zu warten, benutzt man die join() - Methode.

Ein Thread kann mit der sleep()-Methode für eine

vorbestimmte Zeit in den Zustand "not runnable"

versetzt werden.

…… weitere wichtige Methodenweitere wichtige Methoden

Implementierung als Unterklasse von ThreadImplementierung als Unterklasse von Threadpublic 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);}

}

}

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" );

}

}

Ich bin mainIch bin main

Ich bin AndreasIch bin Andreas

Ich bin HannaIch bin Hanna

Ich bin AnneIch bin Anne

Ich bin AndreasIch bin Andreas

Ich bin HannaIch bin Hanna

Ich bin AnneIch bin Anne

Ich bin AndreasIch bin Andreas

Ich bin HannaIch bin Hanna

Ich bin AnneIch bin Anne

Ich bin AndreasIch bin Andreas

Ich bin HannaIch bin Hanna

Ich bin AndreasIch bin Andreas

Ich bin HannaIch bin Hanna

maint1t2t3t1t2t3t1t2t3t1t2t1t2

Ausgabe:

ThreadThread--BeispielBeispiel

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 {

t1.join(); // Warte auf den 1.Threadt2.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 AndreasIch bin Andreas

Ich bin HannaIch bin Hanna

Ich bin AndreasIch bin Andreas

Ich bin HannaIch bin Hanna

Ich bin AndreasIch bin Andreas

Ich bin HannaIch bin Hanna

Ich bin AndreasIch bin Andreas

Ich bin HannaIch bin Hanna

Ich bin AndreasIch bin Andreas

Ich bin HannaIch bin Hanna

Ich bin mainIch bin main

t1

t2

t1

t2

t1

t2

t1

t2

t1

t2

main

garbage collector

garbage collector

Es gibt kein JavaEs gibt kein Java--Programm ohne Threads.Programm ohne Threads.

Java-Anwendung

Betriebssystem

Browser

Java-Applet Java-Appletmain-Thread

init-Thread init-Thread

Betriebssystem

setVisible(true)

event-dispatcher

GUI

Gliederung der VorlesungGliederung der Vorlesung

- Konzepte der Nebenläufigkeit

- Threads in Java

-- SynchronisationsproblemeSynchronisationsprobleme

- Klassische Lösungen

- Semaphoren

- Monitore

- Lebenszyklus eines Threads

Was ist das Problem mit Threads?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?

x=y

Threads mit gemeinsamen DatenThreads mit gemeinsamen DatenT1

x=y

T2

T1: x = x + 1;

T2:x = x * 2;

T2:y = y * 2;

public void run(){..x = x + 1;y = y + 1;

.}

paint();

public void run(){..x = x * 2;y = y * 2;

.}

paint();

paint(); T2:T1: y = y + 1;

3, 34, 38, 38, 68, 68, 7

x y

SynchronisationsproblemeSynchronisationsprobleme

public class Asynchron extends Thread {

public static int zaehler = 0;

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();

}

}

Klassenvariable

Gemeinsame

Variable für

alle Objekte

der Klasse

Asynchron.

SynchronisationsproblemeSynchronisationsprobleme

public void run() {while ( zaehler<25 ) {

zaehler++;System.out.print( this + " " );System.out.println( zaehler );

}}

public void run() {while ( zaehler<25 ) {

zaehler++;System.out.print( this + " " );System.out.println( zaehler );

}}

zaehler 0

t1 t2

SynchronisationsproblemeSynchronisationsprobleme

zaehler 6

t0 t1

public void run() {while ( zaehler<25 ) {

zaehler++;System.out.print( this + " " );System.out.println( zaehler );

}}

public void run() {while ( zaehler<25 ) {

zaehler++;System.out.print( this + " " );System.out.println( zaehler );

}}

78

SynchronisationsproblemeSynchronisationsproblemeThread[Thread-0,5,main] 1Thread[Thread-0,5,main] 2Thread[Thread-0,5,main] 3Thread[Thread-0,5,main] 4Thread[Thread-0,5,main] 5Thread[Thread-0,5,main] 6Thread[Thread-1,5,main] Thread[Thread-0,5,main] 88Thread[Thread-0,5,main] 9Thread[Thread-0,5,main] 10Thread[Thread-0,5,main] 11Thread[Thread-0,5,main] 12Thread[Thread-0,5,main] 13Thread[Thread-0,5,main] 14Thread[Thread-0,5,main] 15Thread[Thread-0,5,main] 16Thread[Thread-0,5,main] 17Thread[Thread-0,5,main] 18Thread[Thread-0,5,main] Thread[Thread-1,5,main] 2020Thread[Thread-0,5,main] Thread[Thread-1,5,main] 2222Thread[Thread-0,5,main] Thread[Thread-1,5,main] 24Thread[Thread-0,5,main] 25

SynchronisationsproblemeSynchronisationsprobleme

zaehler 24

t0 t1

public void run() {while ( zaehler<25 ) {

zaehler++;System.out.print( this + " " );System.out.println( zaehler );

}}

public void run() {while ( zaehler<25 ) {

zaehler++;System.out.print( this + " " );System.out.println( zaehler );

}}

SynchronisationsproblemeSynchronisationsprobleme

zaehler 24

t0 t1

public void run() {while ( zaehler<25 ) {

zaehler++;System.out.print( this + " " );System.out.println( zaehler );

}}

public void run() {while ( zaehler<25 ) {

zaehler++;System.out.print( this + " " );System.out.println( zaehler );

}}

2526

LLöösung:sung:

T1

public void run(){..x = x + 1;y = y + 1;

.}

paint();

T2

public void run(){..x = x * 2;y = y * 2;

.}

paint();

Kritischer Abschnitt

Kritischer Abschnitt

nur ein Thread im kritischen Abschnitt

Wechselseitiger Ausschluss muss gewährleistet werden.

kritischer Abschnitt

Wechselseitiger Ausschluss muss gewährleistet werden,

wenn gemeinsame Ressourcen benutzt werden.

Kritische AbschnitteKritische Abschnitte

simultaneous peripheral operations on line

Drucker-Spooler

Erster freier Platz der Warteschlange

QTail

Thread A

...adr = liest QTail

speichert Datei in adr

inkrementiert QTail

...

Thread B

...adr = liest QTail

speichert Datei in adr

inkrementiert QTail

...

Typisches Beispiel:

0 1 2 3 4

Klassisches Beispiel in der Literatur:

simultaneous peripheral operations on line

Drucker-Spooler

0 1 2 3

QTail

Thread A

...adr = liest QTail

speichert Datei in adr

inkrementiert QTail

...

Thread B

...adr = liest QTail

speichert Datei in adr

inkrementiert QTail

...

Unterbrechung

=4

Unterbrechung

4 5

44

Typisches Beispiel:simultaneous peripheral operations on line

Drucker-Spooler

0 1 2 3

QTail

Thread A

...adr = liest QTail

speichert Datei in adr

inkrementiert QTail

...

Thread B

...adr = liest QTail

speichert Datei in adr

inkrementiert QTail

...Unterbrechung

4 5

Kritischer Abschnitt

Unterbrechung

6

Gliederung der VorlesungGliederung der Vorlesung

- Konzepte der Nebenläufigkeit

- Threads in Java

- Synchronisationsprobleme

-- Klassische LKlassische Löösungensungen

- Semaphoren

- Monitore

- Lebenszyklus eines Threads

Kritische AbschnitteKritische AbschnitteKritische Abschnitte sind die Teile des Programms, in denen auf gemeinsam benutzten Speicher bzw. Ressourcen zugegriffen werden kann.

Wechselseitiger Ausschluss muss gewährleistet werden.

1. Unterbrechungen ausschalten

2. Variablen sperren

3. Strikter Wechsel

4. Petersons Lösung

5. Die TSL- Anweisung

Wie?

1. Unterbrechungen ausschalten

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.

disable_Interrupts();

kritischer_abschnitt();

enable_Interrupts();

2. Variablen sperren

Warteschlange

Sperre = 0

Wenn Sperre = 0, ist die Warteschlange gesperrt

Wenn Sperre = 1, ist die Warteschlange nicht gesperrt

Nehmen wir an, zwei Threads möchten eine Warteschlange

gemeinsam benutzen.

Die Prozesse benutzen wiederum eine gemeinsame Variable, um den Zugriff auf die Warteschlange zu synchronisieren.

2. Variablen sperren

Warteschlange

Sperre =

Thread A Thread B

if (Sperre == 0)

Sperre = 1;

kritischer

Abschnitt

wird

ausgeführt

else

warten....

if (Sperre == 0)

Sperre = 1;

kritischer

Abschnitt

wird

ausgeführt

else

warten....

Interrupt

0

2. Variablen sperren

Warteschlange

Sperre =

Thread A Thread B

if (Sperre == 0)

Sperre = 1;

kritischer

Abschnitt

wird

ausgeführt

else

warten....

if (Sperre == 0)

Sperre = 1;

kritischer

Abschnitt

wird

ausgeführt

else

warten....

Interrupt

Interrupt

1

2. Variablen sperren

Warteschlange

Sperre =

Thread A Thread B

if (Sperre == 0)

Sperre = 1;

kritischer

Abschnitt

wird

ausgeführt

else

warten....

if (Sperre == 0)

Sperre = 1;

kritischer

Abschnitt

wird

ausgeführt

else

warten....

Interrupt

1

Beide Prozesse können in ihren

kritischen Bereich

gleichzeitig eintreten.

3. Strikter Wechsel

turn = 0

while ( true ) {

while ( turn != 0 );

critical_region();

turn = 1;

noncritical_region();

}

while ( true ) {

while ( turn != 1 );

critical_region();

turn = 0;

noncritical_region();

}

Warteschlange

Thread 0 Thread 1

3. Strikter Wechsel

turn = 0

while ( true ) {

while ( turn != 0 );

critical_region();

turn = 1;

noncritical_region();

}

while ( true ) {

while ( turn != 1 );

critical_region();

turn = 0;

noncritical_region();

}

Warteschlange

Thread 0 Thread 1

3. Strikter Wechsel

turn = 1

while ( true ) {

while ( turn != 0 );

critical_region();

turn = 1;

noncritical_region();

}

while ( true ) {

while ( turn != 1 );

critical_region();

turn = 0;

noncritical_region();

}

Warteschlange

Thread 0 Thread 1

3. Strikter Wechsel

turn = 1

while ( true ) {

while ( turn != 0 );

critical_region();

turn = 1;

noncritical_region();

}

while ( true ) {

while ( turn != 1 );

critical_region();

turn = 0;

noncritical_region();

}

Warteschlange

Thread 0 Thread 1

3. Strikter Wechsel

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();

}

turn = 1

Warteschlange

Thread 0 Thread 1

Erst wenn Thread B wieder in seinem kritischen Abschnitt gewesen ist, darf Thread A seinen kritischen Abschnitt wieder betreten.

4. Petersons Lösung für zwei Prozessepublic 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();

}}

}

4. Petersons Lösung für zwei Prozesse

ready

0 1

false false turn =

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 }

}

}

0

Thread 0 Thread 1

4. Petersons Lösung für zwei Prozesse

ready

0 1

true false turn =

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 }

}

}

1

Thread 0 Thread 1

4. Petersons Lösung für zwei Prozesse

ready

0 1

true turn =

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 }

}

}

0false

Thread 0 Thread 1

5. TSL-Anweisung

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.

Lösung mit Hardware-Unterstützung.

Peterson + TSL-Anweisung

while ( true ) {

flag[0] = true ;

turn = 1;

while(flag[1] && turn==1);

{ //kritischer Abschnitt }

flag[0] = false;

{ //Rest des Programms }

}

while ( true ) {

flag[1] = true ;

turn = 0;

while(flag[0] && turn==0);

{ //kritischer Abschnitt }

flag[1] = false;

{ //Rest des Programms }

}

P0 hat eine höhere Priorität P1 hat eine niedrigere Priorität

Prioritätsumkehrproblem

flag

0 1

false false turn = 0

Peterson + TSL-Anweisung

while ( true ) {

flag[0] = true ;

turn = 1;

while(flag[1] && turn==1);

{ //kritischer Abschnitt }

flag[0] = false;

{ //Rest des Programms }

}

while ( true ) {

flag[1] = true ;

turn = 0;

while(flag[0] && turn==0);

{ //kritischer Abschnitt }

flag[1] = false;

{ //Rest des Programms }

}

P0 hat eine höhere Priorität P1 hat eine niedrigere Priorität

Prioritätsumkehrproblem

flag

0 1

true true turn = 1

Peterson + TSL-Anweisung

Der Peterson-Algorithmus und die TSL-Lösung haben

den Nachteil, dass aktives Warten erforderlich ist.

1. CPU-Zeit Verschwendung

2. PrioritätsumkehrproblemProbleme

Prioritätsumkehrproblem

Obwohl P0 eine höhere Priorität als P1 hat, muss er

immer länger als P1 warten, um in seinen kritischen

Abschnitt eintreten zu können.

Schlafen und AufweckenSleep 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.

Gliederung der VorlesungGliederung der Vorlesung

- Konzepte der Nebenläufigkeit

- Threads in Java

- Synchronisationsprobleme

- Klassische Lösungen

-- SemaphorenSemaphoren

- Monitore

- Lebenszyklus eines Threads

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

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.

Lösung mit Semaphoren

Semaphore sem = new Semaphore(1);. . .public void run(){

. . .sem.acquire();kritische_abschnitt();sem.release();. . .

}. . .

Ab Java 1.5 ist eine Semaphor-Klasse in dem Paket java.util.concurrent enthalten.

Allgemeine Verwendung eines einfachen binären

Semaphors oder mutex.

Probleme mit SemaphorenProbleme mit Semaphoren

s.release();

. . .

// Kritischer Abschnitt

. . .

s.acquire();

Mehrere Threads können in den kritischen Abschnitt eintreten.

s.acquire();

. . .

// Kritischer Abschnitt

. . .

s.acquire();

Ein Deadlockfindet innerhalb des Threads statt.

Folgende einfache Fehler werden oft vom Programmierer gemacht:

Probleme mit SemaphorenProbleme mit Semaphoren

S1.acquire();

S2.acquire();

.

.

.

S2.release();

S1.release();

T0 T1

S2.acquire();

S1.acquire();

.

.

.

S1.release();

S2.release();

Folgende Fehler sind im Programm schwer zu erkennen, weil die Probleme nur bei bestimmten Ausführungssequenzen auftreten.

S1.acquire();

S2.acquire();

T0:

T1:

T0:

T1:

S2.acquire();

S1.acquire();

Wartet!

Wartet!

Verklemmung!Verklemmung!

Gliederung der VorlesungGliederung der Vorlesung

- Konzepte der Nebenläufigkeit

- Threads in Java

- Synchronisationsprobleme

- Klassische Lösungen

- Semaphoren

-- MonitoreMonitore

- Lebenszyklus eines Threads

MonitoreMonitore

Gemeinsame Daten

Sind abstrakte Datentypen (ADT) Ta Tb Tc Td

.

.

.

Operationen

Ein Monitor garantiert gegenseitigen Ausschluss des Zugriffs auf die Daten innerhalb des Monitors.

Nur durch die Operationen des Monitors dürfen die inneren Daten geändert werden. Nur ein Thread darf

zu einem bestimmten Zeitpunkt innerhalb des Monitors aktiv sein.

MonitoreMonitore

To

Das Objekt ist blockiert

T3 T2 T4 T8 T1

Besitzer des Objektes

Nur ein Thread darf zu einem bestimmten Zeitpunkt innerhalb des Monitors aktiv sein.

Monitore können in Java als Klassen mit synchronized-

Methoden und geeigneter Verwendung der wait-, notify- und

notifyAll-Methoden realisiert werden.

Das Das synchronizedsynchronized--SchlSchlüüsselwortsselwort in Javain Java

In Java ist jede Instanz der Klasse java.lang.Objectmit einer Sperrvariable verknüpft.

Diese Sperre entspricht einem booleschen Semaphor.

. . .synchronized ( nums ) {

for ( int i=0; i<nums.length; i++ ) {if (nums[i] < 0)

nums[i] = -nums[i];}

}. . .

Beispiel:

. . .synchronized (Objektvariable) {

. . .}. . .

Das Das synchronizedsynchronized--SchlSchlüüsselwortsselwort in Javain Java

T1

. . .synchronized (A) {

. . .synchronized (B) {

. . .}

}. . .

T2

. . .synchronized (B) {

. . .synchronized (A) {

. . .}

}. . .

Verklemmung!

MonitoreMonitore

Gemeinsame Daten

Ta Tb Tc Td

Bedingungsvariablen

b1

b2

b3

T2 T1 T3

.

.

.

Warteschlangen, die mit Bedingungsvariablen verbunden sind.

Sprachen mit Monitorkonzept

Concurrent Pascal

C#

Erlang

Ada

Squeak Smalltalk

Java

T7

T4 T5

WaitWait undund NotifyNotify

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 Warteschlangedes 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.

Monitore in JavaMonitore in Java

To

Das Objekt ist blockiert

T3

Besitzer des Objektes

T2 T4T8 T1T8 T1

Entry-Set Wait-Set

Hier warten die Threads auf den Lock des Objektes

Hier warten die Threads auf ein Notify

wait

Monitore in JavaMonitore in Java

To

Das Objekt ist blockiert

T3

Besitzer des Objektes

T2 T4T8 T1T8 T1

Entry-Set Wait-Set

Hier warten die Threads auf den Lock des Objektes

Hier warten die Threads auf ein Notify

notify

Leser/SchreiberLeser/Schreiber--ProblemProblem

public class Database implements ReadWriteLock {

private int readerCounter;

private boolean Writing;

public Database(){

readerCounter = 0;

Writing = false;

}

. . .

}//end of class Database

- beliebig viele Threads dürfen gleichzeitig lesen

- nur ein Thread darf schreiben (keiner darf lesen)

Leser/SchreiberLeser/Schreiber--ProblemProblempublic 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

Leser/SchreiberLeser/Schreiber--ProblemProblem

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!

Gliederung der VorlesungGliederung der Vorlesung

- Konzepte der Nebenläufigkeit

- Threads in Java

- Synchronisationsprobleme

- Klassische Lösungen

- Semaphoren

- Monitore

-- Lebenszyklus eines ThreadsLebenszyklus eines Threads

NewNew

new Thread()

rechenrechen--bereitbereit

TotTot

start()

stop()

blockiertblockiertresume()

stop()

suspend()

LebensLebens--Zyklus einesZyklus eines ThreadThreadss JDK 1.1JDK 1.1

stop()

Deadlock-Probleme!

kritischer Abschnitt

Verklemmungsprobleme

stop

Verklemmung!

rechenrechen--bereitbereit

new Thread()

rechnendrechnend

MonitorMonitor--blockiertblockiert

start()tottot

LebensLebens--Zyklus einesZyklus eines ThreadThreadss JDK 1.2JDK 1.2

wartendwartend

schlafendschlafendIOIO--

blockiertblockiert

neuneu

yield()

SchedulingScheduling

terminiert

wait()

notify()

interrupt

interrupt

Frei-gegeben

Monitorbesetzt

Timeout IO-Operation

IO-End

sleep()

java.util.concurrentjava.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

LiteraturLiteratur

Operating System Concepts with Java. 7th Edition. 2007

Silverschatz, Galvin und Gagne

Java Threads. 3th Edition. 2004

Scott Oaks & Henry Wong.

FragenFragen