Systemy Rozproszone - Ćwiczenie 4
1 Synchronizacja wątków: myjnia samochodowa
Poniżej znajduje się przykład ilustrujący klasyczny problem synchronizacji wąt- ków. Obiekt klasy Car jest współdzielony przez 2 wątki. Samochód może znaj- dować się w jednym w 2 stanów: nawoskowany (waxed==true) i wypolerowany (waxed==false). Na samochodzie działają 2 wątki: Waxer, którego zadaniem jest nawoskowanie samochodu, oraz Polisher, którego zadaniem jest jest wypole- rowanie samochodu. Nawoskowanie samochodu jest możliwe tylko wtedy jeżeli samochód jest wypolerowany (czyli ma stan waxed==false) i skutkuje zmianą jego stanu na waxed==true. Wypolerowanie samochodu jest możliwe tylko wte- dy jeżeli samochód jest nawoskowany (czyli ma stan waxed==true) i skutkuje jego zmianą stanu na waxed==false. Obydwa wątki wykonują swoje działania w nieskończonej pętli, a oczekiwanie wątku na osiągnięcie właściwego stanu przez samochód odbywa się w metodach waitUntilWaxed() i waitUntilPolished().
Wykonaniu działana na samochodzie (metody polish() i wax()) skutkuje zmia- ną jego stanu, i wznowieniem pracy przez oczekujący wątek. Zawieszenie pra- cy wątku uzyskuje się wywołując metodę Thread.wait(), a wznowienie pra- cy wszystkich oczekujących wątków uzyskuje się poprzez wywołanie metody Thread.notifyAll(). Wątek główny, po uruchomieniu wątków Waxer i Polisher,
odczekuje 100ms Thread.sleep(), a następnie przerywa pracę 2 wątków Thread.interrupt().
/∗ p l i k : WaxOMatic . j a v a ∗/
c l a s s Car {
boolean waxed = f a l s e ;
public synchronized void wax ( ) throws I n t e r r u p t e d E x c e p t i o n { w a i t U n t i l P o l i s h e d ( ) ;
waxed = true ; n o t i f y A l l ( ) ; }
public synchronized void p o l i s h ( ) throws I n t e r r u p t e d E x c e p t i o n { waitUntilWaxed ( ) ;
waxed = f a l s e ; n o t i f y A l l ( ) ; }
public synchronized boolean isWaxed ( ) { return waxed ;
}
private synchronized void waitUntilWaxed ( ) throws I n t e r r u p t e d E x c e p t i o n { while ( ! isWaxed ( ) ) {
System . o u t . p r i n t l n ( Thread . c u r r e n t T h r e a d ( ) . getName ( ) + " ␣ i s ␣ w a i t i n g ␣ u n t i l ␣ waxed " ) ;
w a i t ( ) ; }
}
private synchronized void w a i t U n t i l P o l i s h e d ( ) throws I n t e r r u p t e d E x c e p t i o n { while ( isWaxed ( ) ) {
System . o u t . p r i n t l n ( Thread . c u r r e n t T h r e a d ( ) . getName ( ) + " ␣ i s ␣ w a i t i n g ␣ u n t i l ␣ p o l i s h e d " ) ;
w a i t ( ) ; }
} }
c l a s s Waxer extends Thread { Car c a r ;
public Waxer ( Car c ) { c a r = c ;
}
public void run ( ) { try {
while ( true ) {
System . ou t . p r i n t l n ( Thread . c u r r e n t T h r e a d ( ) . getName ( ) + " ␣ i s ␣ waxing . . . " ) ;
c a r . wax ( ) ;
System . ou t . p r i n t l n ( Thread . c u r r e n t T h r e a d ( ) . getName ( ) + " ␣ f i n i s h e d ␣ waxing " ) ;
} }
catch ( I n t e r r u p t e d E x c e p t i o n e ) {
System . o u t . p r i n t l n ( "Waxer␣ i n t e r r u p t e d " ) ; }
} }
c l a s s P o l i s h e r extends Thread { Car c a r ;
public P o l i s h e r ( Car c ) {
c a r = c ; }
public void run ( ) { try {
while ( true ) {
System . ou t . p r i n t l n ( Thread . c u r r e n t T h r e a d ( ) . getName ( ) + " ␣ i s ␣ p o l i s h i n g . . . " ) ;
c a r . p o l i s h ( ) ;
System . ou t . p r i n t l n ( Thread . c u r r e n t T h r e a d ( ) . getName ( ) + " ␣ f i n i s h e d ␣ p o l i s h i n g " ) ;
} }
catch ( I n t e r r u p t e d E x c e p t i o n e ) {
System . o u t . p r i n t l n ( " P o l i s h e r ␣ i n t e r r u p t e d " ) ; }
} }
public c l a s s WaxOMatic {
public s t a t i c void main ( S t r i n g [ ] a r g s ) throws E x c e p t i o n { Car c a r = new Car ( ) ;
Waxer waxer = new Waxer ( c a r ) ;
P o l i s h e r p o l i s h e r = new P o l i s h e r ( c a r ) ; waxer . s t a r t ( ) ;
p o l i s h e r . s t a r t ( ) ; Thread . s l e e p ( 1 0 0 ) ; waxer . i n t e r r u p t ( ) ; p o l i s h e r . i n t e r r u p t ( ) ; }
}
Wprowadź następujące modyfikacje do programu:
• Dodaj opóznienie do metod wax() i polish polish() symulujące czas wy- konania działania na samochodzie. Jak wpłynie to na działanie programu.
• Wprowadz dodatkowy wątek do programu Washer, którego zadaniem jest mycie samochodu, w taki sposób, żeby samochód zmieniał stany w nastę- pujący sposób: waxed →polished→washed→waxed→polished→washed→
. . ..
2 Problem producenta i konsumenta
Kolejny przykład ilustruje klasycznyproblem producenta i konsumentaw odnie- sieniu do dwóch wątków działających na współdzielonym stosie WaitingStack.
Pierwszy wątek, StackPusher, pełni rolę producenta i jego zadaniem jest wkła- danie elementów na stos. Drugi wątek, StackPopper, pełni rolę konsumenta i jego zadaniem jest zdejmowania elementów ze stosu. Zwróć uwagę na metody waitForNotEmpty(), waitForFullEmpty(), push(), pop().
/∗ p l i k : W a i t i n g S t a c k . j a v a ∗/
c l a s s W a i t i n g S t a c k { i n t [ ] d a t a ;
i n t c a p a c i t y ; i n t c u r r e n t = −1;
public W a i t i n g S t a c k ( i n t c o u n t ) { c a p a c i t y = c o u n t ;
d a t a = new i n t [ c o u n t ] ; }
public synchronized void push ( i n t number ) throws I n t e r r u p t e d E x c e p t i o n {
S t r i n g name = Thread . c u r r e n t T h r e a d ( ) . getName ( ) ; System . ou t . f o r m a t ( "%s : ␣ e n t e r e d ␣ push ( ) \ n" , name ) ; w a i t F o r N o t F u l l ( ) ;
System . ou t . f o r m a t ( "%s : ␣ can ␣ push ( ) \ n" , name ) ; c u r r e n t ++;
d a t a [ c u r r e n t ] = number ;
System . ou t . f o r m a t ( "%s ␣ pushed ␣%d␣ t o ␣[%d ] \ n" , name , number , c u r r e n t ) ; n o t i f y A l l ( ) ;
System . ou t . f o r m a t ( "%s : ␣ ended ␣ push ( ) \ n" , name ) ; }
public synchronized i n t pop ( ) throws I n t e r r u p t e d E x c e p t i o n {
S t r i n g name = Thread . c u r r e n t T h r e a d ( ) . getName ( ) ; System . ou t . f o r m a t ( "%s : ␣ e n t e r e d ␣ pop ( ) \ n" , name ) ; waitForNotEmpty ( ) ;
System . ou t . f o r m a t ( "%s : ␣ can ␣ pop \n" , name ) ; i n t v a l u e = d a t a [ c u r r e n t ] ;
System . ou t . f o r m a t ( "%s : ␣ popped ␣%d␣ from ␣[%d ] \ n" , name , v a l u e , c u r r e n t ) ; c u r r e n t −−;
n o t i f y A l l ( ) ;
System . ou t . f o r m a t ( "%s : ␣ ended ␣ pop ( ) \ n" , name ) ; return v a l u e ;
}
private synchronized void waitForNotEmpty ( ) throws I n t e r r u p t e d E x c e p t i o n {
while ( c u r r e n t ==−1) {
System . o u t . p r i n t l n ( Thread . c u r r e n t T h r e a d ( ) . getName ( ) + " ␣ i s ␣ w a i t i n g ␣ t o ␣ pop " ) ;
w a i t ( ) ; }
}
private synchronized void w a i t F o r N o t F u l l ( ) throws I n t e r r u p t e d E x c e p t i o n {
while ( c u r r e n t==c a p a c i t y −1) {
System . o u t . p r i n t l n ( Thread . c u r r e n t T h r e a d ( ) . getName ( ) + " ␣ i s ␣ w a i t i n g ␣ t o ␣ push " ) ;
w a i t ( ) ; }
} }
c l a s s S t a c k P u s h e r extends Thread { W a i t i n g S t a c k s t a c k ;
i n t v a l u e = 0 ;
public S t a c k P u s h e r ( W a i t i n g S t a c k ws ) { s t a c k = ws ;
}
public void run ( ) { try {
while ( true ) { s l e e p ( 1 0 0 ) ;
s t a c k . push(++v a l u e ) ; y i e l d ( ) ;
} }
catch ( I n t e r r u p t e d E x c e p t i o n e ) {
System . o u t . p r i n t l n ( Thread . c u r r e n t T h r e a d ( ) . getName ( ) + " ␣ i n t e r r u p t e d " ) ;
} } }
c l a s s StackPopper extends Thread { W a i t i n g S t a c k s t a c k ;
public StackPopper ( W a i t i n g S t a c k ws ) { s t a c k = ws ;
}
public void run ( ) { try {
while ( true ) { s t a c k . pop ( ) ; y i e l d ( ) ; }
}
catch ( I n t e r r u p t e d E x c e p t i o n e ) {
System . o u t . p r i n t l n ( Thread . c u r r e n t T h r e a d ( ) . getName ( ) + " ␣ i n t e r r u p t e d " ) ;
} } }
public c l a s s WaitingStackDemo {
public s t a t i c void main ( S t r i n g [ ] a r g s ) throws E x c e p t i o n { W a i t i n g S t a c k ws = new W a i t i n g S t a c k ( 1 0 ) ;
S t a c k P u s h e r pu = new S t a c k P u s h e r ( ws ) ; StackPopper po1 = new StackPopper ( ws ) ; StackPopper po2 = new StackPopper ( ws ) ; StackPopper po3 = new StackPopper ( ws ) ; pu . s t a r t ( ) ;
po1 . s t a r t ( ) ; po2 . s t a r t ( ) ; po3 . s t a r t ( ) ;
Thread . s l e e p ( 1 0 0 0 ) ; pu . i n t e r r u p t ( ) ; po1 . i n t e r r u p t ( ) ; po2 . i n t e r r u p t ( ) ; po3 . i n t e r r u p t ( ) ; }
}
3 Bar mleczny "Prawo dżungli"
Rozważ producenta i konsumenta na przykładzie baru mlecznego wydającego posiłki wiecznie głodnym klientom. Producentem jest kucharz umieszczający dania na ladzie, konsumentami są klienci przebywający w barze. Wiecznie głod- ni klienci, których liczba n jest stała, konsumują wydawane potrawy na zasadzie
"kto pierwszy ten lepszy". Zaimplementuj powyższy przykład przy użyciu wąt- ków. Po zakończeniu programu wypisz ile potraw zjadł każdy z klientów.