• Keine Ergebnisse gefunden

sleeping sleep// ... time over// ... completedjoin readyrunningdeadjoining startyield

N/A
N/A
Protected

Academic year: 2022

Aktie "sleeping sleep// ... time over// ... completedjoin readyrunningdeadjoining startyield"

Copied!
67
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

ready

running

dead joining

start

yield

sleeping

sleep

// ... time over

// ... completed

join

(2)

publi lass Join implements Runnable {

private stati int ount = 0;

private int n = ount++;

private stati Thread[℄ task = new Thread[3℄;

publi void run() {

try {

if (n>0) {

task[n-1℄.join();

System.out.println( "T hre ad- "+ n+" joined Thread-"+(n-1));

}

} ath (InterruptedExep ti on e) {

System.err.println( e.t oS tri ng( )) ;

}

} ...

(3)

publi stati void main(String[℄ args) {

for(int i=0; i<3; i++)

task[i℄ = new Thread(new Join());

for(int i=0; i<3; i++)

task[i℄.start();

}

} // end of lass Join

... liefert:

> java Join

Thread-1 joined Thread-0

Thread-2 joined Thread-1

Spaÿeshalber betrahten wir noh eine kleine Variation des letzten

Programms:

(4)

private stati int ount = 0;

private int n = ount++;

private stati Thread[℄ task = new Thread[3℄;

publi void run() {

try { task[(n+1)%3℄.joi n() ; }

ath (InterruptedExep tio n e) {

System.err.printl n(e .to St rin g( ));

}

}

publi stati void main(String[℄ args) {

for(int i=0; i<3; i++)

task[i℄ = new Thread(new CW());

for(int i=0; i<3; i++) task[i℄.start();

}

(5)

Jeder Thread geht in einen Wartezustand (hier: joining) über und wartet auf einen anderen Thread.

Dieses Phänomen heiÿt auh Cirular Wait oder Deadlok eine

unangenehme Situation, die man in seinen Programmen

tunlihst vermeiden sollte.

(6)

Damit Threads sinnvoll miteinander kooperieren können, müssen sie miteinander Daten austaushen.

Zugri mehrerer Threads auf eine gemeinsame Variable ist

problematish, weil niht feststeht, in welher Reihenfolge die

Threads auf die Variable zugreifen.

Ein Hilfsmittel, um geordnete Zugrie zu garantieren, sind Monitore.

... ein Beispiel:

(7)

private stati int x = 0;

private stati void pause(int t) {

try {

Thread.sleep((in t) (Math.random()*t*10 00 ));

} ath (InterruptedExep ti on e) {

System.err.print ln( e.t oS tri ng( )) ;

}

}

publi void run() {

String s = Thread.urrentThrea d() .g etN ame () ;

pause(3); int y = x;

System.out.printl n( s+ " read "+y);

pause(4); x = y+1;

System.out.printl n( s+ " wrote "+(y+1));

(8)

publi stati void main(String[℄ args) {

(new Thread(new In())).start();

pause(2);

(new Thread(new In())).start();

pause(2);

(new Thread(new In())).start();

}

} // end of lass In

publi stati Thread urrentThread(); liefert (eine Referenz auf) das ausführende Thread-Objekt.

publi final String getName(); liefert den Namen des

Thread-Objekts.

Das Programm legt für drei Objekte der Klasse In Threads an.

Die Methode run() inkrementiert die Klassen-Variable x.

(9)

> java In

Thread-0 read 0

Thread-0 wrote 1

Thread-1 read 1

Thread-2 read 1

Thread-1 wrote 2

Thread-2 wrote 2

Der Grund:

(10)

x Th-0

Th-1

Th-2 y

y

y 0

(11)

x Th-0

Th-1

Th-2 y

y

y 0

y = x;

(12)

x Th-0

Th-1

Th-2 y

y

y 0 0

x = y+1;

(13)

x Th-0

Th-1

Th-2 y

y

y 0 1

y = x;

(14)

x Th-0

Th-1

Th-2 y

y

y 0 1

1

y = x;

(15)

x Th-0

Th-1

Th-2 y

y

y 0 1

1

1

x = y+1;

(16)

x Th-0

Th-1

Th-2 y

y

y 0

1

1

2

x = y+1;

(17)

x Th-0

Th-1

Th-2 y

y

y 0

1

1

2

(18)

Inkrementieren der Variable x sollte ein atomarer Shritt sein, d.h. niht von parallel laufenden Threads unterbrohen werden

können.

Mithilfe des Shlüsselworts synhronized kennzeihnen wir Objekt-Methoden einer Klasse L als ununterbrehbar.

Für jedes Objekt obj der Klasse L kann zu jedem Zeitpunkt nur

ein Aufruf obj.synhMeth(...) einer

synhronized-Methode synhMeth() ausgeführt werden. Die

Ausführung einer solhen Methode nennt man kritishen

Abshnitt (ritial setion) für die gemeinsame Resoure obj.

Wollen mehrere Threads gleihzeitig in ihren kritishen

Abshnitt für das Objekt obj eintreten, werden alle bis auf einen

blokiert.

(19)

ready

running

dead joining

start

yield

sleeping

sleep

// ... time over

// ... completed

join

(20)

ready

running

dead blocked

joining

start

yield

sleeping

sleep

// ... time over

// ... completed

join

(21)

Jedes Objekt obj mit synhronized-Methoden verfügt über:

1. über ein booleshes Flag boolean loked; sowie

2. über eine Warteshlange ThreadQueue blokedThreads.

Vor Betreten seines kritishen Abshnitts führt ein Thread

(implizit) die atomare Operation obj.lok() aus:

private void lok() {

if (!loked) loked = true; // betrete krit. Abshnitt

else { // Lok bereits vergeben

Thread t = Thread.urrentThr ead () ;

blokedThreads.enq ue ue( t);

t.state = bloked; // blokiere

}

} // end of lok()

(22)

Verlässt ein Thread seinen kritishen Abshnitt für obj (evt.

auh mittels einer Exeption), führt er (implizit) die atomare

Operation obj.unlok() aus:

private void unlok() {

if (blokedThreads.e mpt y( ))

loked = false; // Lok frei geben

else { // Lok weiterreihen

Thread t = blokedThreads.de qu eue ();

t.state = ready;

}

} // end of unlok()

Dieses Konzept nennt man Monitor.

(23)

x 1 false locked

count

blocked Th-1

Th-2

Th-0

(24)

x.lock();

x 1

false locked

count

blocked Th-1

Th-2

Th-0

(25)

x.inc();

x 1

locked count

blocked true

Th-1

Th-2

Th-0

(26)

x.inc();

x.lock();

x 1

locked count

blocked true

Th-1

Th-2

Th-0

(27)

x.inc();

x 1

locked count

blocked true

Th-1 Th-0

Th-2

(28)

x.unlock();

x

locked count

blocked true

Th-1

2 Th-0

Th-2

(29)

x.inc();

Th-0

x

locked count

blocked true

Th-1

2

Th-2

(30)

x.unlock();

Th-0

x

locked count

blocked true

Th-1

3

Th-2

(31)

Th-0

x

locked count

blocked Th-1

3 false

Th-2

(32)

private int ount = 0;

publi synhronized void in() {

String s = Thread.urrentThread (). get Nam e() ;

int y = ount; System.out.println( s+ " read "+y);

ount = y+1; System.out.println( s+ " wrote "+(y+1));

}

} // end of lass Count

publi lass InSyn implements Runnable {

private stati Count x = new Count();

publi void run() { x.in(); }

publi stati void main(String[℄ args) {

(new Thread(new InSynh())).start();

(new Thread(new InSynh())).start();

(new Thread(new InSynh())).start();

}

} // end of lass InSyn

(33)

> java InSyn

Thread-0 read 0

Thread-0 wrote 1

Thread-1 read 1

Thread-1 wrote 2

Thread-2 read 2

Thread-2 wrote 3

Ahtung:

Die Operationen lok() und unlok() erfolgen nur, wenn der Thread niht bereits vorher das Lok des Objekts erworben hat.

Ein Thread, der das Lok eines Objekts obj besitzt, kann weitere

Methoden für obj aufrufen, ohne sih selbst zu blokieren.

(34)

Um das zu garantieren, legt ein Thread für jedes Objekt obj, dessen Lok er niht besitzt, aber erwerben will, einen neuen

Zähler an:

int ountLok[obj℄ = 0;

Bei jedem Aufruf einer synhronized-Methode m(...) für obj wird der Zähler inkrementiert, für jedes Verlassen (auh mittels

Exeptions) dekrementiert:

if (0 == ountLok[obj℄++ ) lok();

Ausführung von obj.m(...)

if (--ountLok[obj℄ == 0) unlok();

lok() und unlok() werden nur ausgeführt, wenn (ountLok[obj℄ == 0)

(35)

Produer-Consumer-Problem

Aufgabe:

Zwei Threads hten mehrere/viele Daten-Objekte austaushen.

Der eine Thread erzeugt die Objekte einer Klasse Data

(Produer).

Der andere konsumiert sie (Consumer).

Zur Übergabe dient ein Puer, der eine feste Zahl N von

Data-Objekten aufnehmen kann.

(36)

Producer Consumer

(37)

Producer Consumer

(38)

Producer Consumer

(39)

Producer Consumer

(40)

Producer Consumer

(41)

Producer Consumer

(42)

Wir denieren eine Klasse Buffer2, die (im wesentlihen) aus einem Feld der rihtigen Gröÿe, sowie zwei Verweisen int

first, last zum Einfügen und Entfernen verfügt:

publi lass Buffer2 {

private int ap, free, first, last;

private Data[℄ a;

publi Buffer2(int n) {

free = ap = n; first = last = 0;

a = new Data[n℄;

}

...

Einfügen und Entnehmen sollen synhrone Operationen sein ...

(43)

Was maht der Consumer, wenn der Produer mit der

Produktion niht nahkommt, d.h. der Puer leer ist?

Was maht der Produer, wenn der Consumer mit der

Weiterverarbeitung niht nah kommt, d.h. der Puer voll ist?

Java's Lösungsvorshlag: Warten ...

(44)

Was maht der Consumer, wenn der Produer mit der

Produktion niht nahkommt, d.h. der Puer leer ist?

Was maht der Produer, wenn der Consumer mit der

Weiterverarbeitung niht nah kommt, d.h. der Puer voll ist?

Java's Lösungsvorshlag: Warten ...

(45)

Producer Consumer

(46)

Producer Consumer

(47)

Producer Consumer

(48)

Producer

waiting

Consumer

(49)

Producer

waiting

Consumer

(50)

Producer

waiting

Consumer

(51)

Producer Consumer

(52)

Producer Consumer

(53)

Jedes Objekt (mit synhronized-Methoden) verfügt über eine weitere Shlange ThreadQueue waitingThreads am Objekt

wartender Threads sowie die Objekt-Methoden:

publi final void wait() throws InterruptedExept ion ;

publi final void notify();

publi final void notifyAll();

Diese Methoden dürfen nur für Objekte aufgerufen werden, über

deren Lok der Thread verfügt !!!

Ausführen von wait(); setzt den Zustand des Threads auf

waiting, reiht ihn in eine geeignete Warteshlange ein, und gibt

das aktuelle Lok frei:

(54)

Thread t = Thread.urrentThr ea d() ;

t.state = waiting;

waitingThreads.en que ue (t) ;

unlok();

}

(55)

wait()

blocked

x 1

locked count

Th-1

Th-2 Th-0

waiting

true

(56)

blocked

x 1

locked count

Th-1 Th-0

waiting Th-2

true

(57)

blocked

x 1

locked count

Th-1 Th-0

waiting false

Th-2

(58)

Ausführen von notify(); wekt den ersten Thread in der

Warteshlange auf, d.h. versetzt ihn in den Zustand bloked und

fügt ihn in blokedThreads ein:

publi void notify() {

if (!waitingThreads. is Emp ty( )) {

Thread t = waitingThreads.dequ eue () ;

t.state = bloked;

Thread t = blokedThreads.enqu eue () ;

}

}

notifyAll(); wekt alle wartenden Threads auf d.h. fügt alle in blokedThreads ein:

publi void notifyAll() {

while (!waitingThreads. is Emp ty( )) notify();

(59)

Ausführen von notify(); wekt den ersten Thread in der

Warteshlange auf, d.h. versetzt ihn in den Zustand bloked und

fügt ihn in blokedThreads ein:

publi void notify() {

if (!waitingThreads. is Emp ty( )) {

Thread t = waitingThreads.dequ eue () ;

t.state = bloked;

Thread t = blokedThreads.enqu eue () ;

}

}

notifyAll(); wekt alle wartenden Threads auf:

publi void notifyAll() {

while (!waitingThreads.is Emp ty ()) notify();

}

(60)

notify()

blocked

x 1

locked count

Th-1 Th-0

waiting true

Th-2

(61)

blocked Th-2

x 1

locked count

Th-1 Th-0

waiting

true

(62)

ready

running

dead blocked

joining

start

yield

sleeping

sleep

// ... time over

// ... completed

join

(63)

ready

running

dead blocked

joining

start

yield

sleeping waiting

sleep

// ... time over

// ... completed notify

notifyAll

wait

join

(64)

...

publi synhronized void produe(Data d) throws InterruptedExeptio n {

if (free==0) wait(); free--;

a[last℄ = d;

last = (last+1)%ap;

notify();

}

publi synhronized Data onsume() throws InterruptedExept ion {

if (free==ap) wait(); free++;

Data result = a[first℄;

first = (first+1)%ap;

notify(); return result;

}

} // end of lass Buffer2

(65)

Ist der Puer voll, d.h. keine Zelle frei, legt sih der Produer

shlafen.

Ist der Puer leer, d.h. alle Zellen frei, legt sih der Consumer

shlafen.

Gibt es für einen Puer genau einen Produer und einen

Consumer, wekt das notify() des Consumers (wenn

überhaupt, dann) stets den Produer ...

... und umgekehrt.

Was aber, wenn es mehrere Produers gibt? Oder mehrere

Consumers ???

(66)

Teste nah dem Aufweken erneut, ob Zellen frei sind.

Weke niht einen, sondern alle wartenden Threads auf ...

...

publi synhronized void produe(Data d)

throws InterruptedExepti on {

while (free==0) wait(); free--;

a[last℄ = d;

last = (last+1)%ap;

notifyAll();

}

...

(67)

publi synhronized Data onsume() throws InterruptedExepti on {

while (free==ap) wait();

free++;

Data result = a[first℄;

first = (first+1)%ap;

notifyAll();

return result;

}

} // end of lass Buffer2

Wenn ein Platz im Puer frei wird, werden sämtlihe Threads

aufgewekt obwohl evt. nur einer der Produer bzw. nur einer

der Consumer aktiv werden kann.

Referenzen

ÄHNLICHE DOKUMENTE

Mir ist bekannt, dass falsche Angaben als Falschbeurkundung oder Betrug nach den Bestimmungen der §§ 263, 264, 271 und 272 des Strafgesetzbuch verfolgt und bestraft werden

Diese Bestimmung soll also sicherstellen, dass diese Organe über einen Raum für Überlegungen verfügen, um einen Beschluss über die zu treffenden politischen Entscheidungen und

Die- ses soll insbesondere durch bedarfs- gerechte Expertisebereitstellung mit Schaffung von Kompetenzzentren und einem Netzwerk in der Gesund- heitsregion Carus Consilium

Gleichzeitig wurde der mit falschen Anreizen behaftete Finanzausgleich zwischen Kanton und Gemeinden durch einen neuen Finanz- und Lastenausgleich ersetzt, dies nicht zuletzt

Die OB hatte am frühen Morgen in den Nachrichten von dem mutmaßlichen Anschlag erfahren, der nach aktuellem Kenntnisstand acht Todesopfer forderte. Rund 60 Menschen wurden

Sollte im übrigen eine weitere Stadt im Kanton Bern einen entsprechenden Bedarf für eine Kontakt – und Anlaufstelle ausweisen und den klaren politischen Willen für deren

☐ ich mit der Verarbeitung und Nutzung meiner im Pilotprojekt Telemedizin-Assistenz (TMA) erhobenen Daten im Rahmen einer Evaluation (wissenschaftliche Auswertung von Daten) durch

Daselbst befindet sich insonderheit ein großer Liebhaber aller Kunstsachen Heinrich Lochmann,l434 bey deme zu sehen sind viel Erdgewächse, fremde Thiere, ausgelehrte Vögel,