SR lab 3 PGorecki
Transkrypt
SR lab 3 PGorecki
Systemy Rozproszone - Ćwiczenie 3 1 Współbieżny dostęp do obiektu Poniżej znajduje się bardzo prosta klasa - licznik, mający 2 metody: inc() zwiększający licznik o 1 i getValue() zwracający aktualną wartość licznika. W ramach rozgrzewki napisz program, który będzie zwiększać licznik przez 2 sekundy, a po 2 sekundach wypisze wartość licznika. Do tego celu można np. wykorzytać System.currentTimeMillis(). /∗ p l i k SimpleCounter . j a v a ∗/ public c l a s s SimpleCounter { private int v a l u e ; public SimpleCounter ( ) { value = 0; } public void i n c ( ) { v a l u e ++; } public int g e t V a l u e ( ) { return v a l u e ; } } Następnie stworzymy program, który utworzy 3 wątki i 1 obiekt SimpleCounter. W konstruktorze każdemu wątkowi przekazana zostanje referencja do obiektu SimpleCounter. Każdy proces zwiększa licznik 10 razy. Zastanów się co ostatecznie wypisze program na wyjściu, a następnie sprawdź to uruchomiając program. public c l a s s SyncDemo extends Thread { SimpleCounter s o ; public SyncDemo ( SimpleCounter o b j ) { so = obj ; } 1 public void run ( ) { f o r ( int i =0; i <10; i ++) { so . inc ( ) ; } } public s t a t i c void main ( S t r i n g a r g s [ ] ) { SimpleCounter c o u n t e r = new SimpleCounter ( ) ; SyncDemo t h r e a d 1 = new SyncDemo ( c o u n t e r ) ; SyncDemo t h r e a d 2 = new SyncDemo ( c o u n t e r ) ; SyncDemo t h r e a d 3 = new SyncDemo ( c o u n t e r ) ; thread1 . s t a r t ( ) ; thread2 . s t a r t ( ) ; thread3 . s t a r t ( ) ; try { thread1 . j o i n ( ) ; thread2 . j o i n ( ) ; thread3 . j o i n ( ) ; } catch ( I n t e r r u p t e d E x c e p t i o n e ) { System . out . p r i n t l n ( " I n t e r r u p t e d " ) ; } System . out . p r i n t l n ( c o u n t e r . g e t V a l u e ( ) ) ; } } Zwiększ ilość iteracji pętli w metodzie run() do 100, 1000, 100000. Przetestuj działanie programu dla różnej liczby wątków (od 1 do 10). Czy wyniki są zgodne z oczekiwaniami? Co mogło pójść nie tak? Zastanów się nad tym wspólnie z kolegą siedzącym obok Ciebie. Podpowiedź znajduje się na następnej stronie. 2 Co poszło nie tak? Wszystkie 3 procesy wykonywały równolegle metodę inc(). Linia value++ to tak naprawdę 3 instrukcje atomowe: • skopiowanie zmiennej do rejestru: LD R,value • zwiększ rejestr: INC R • skopiowanie rejestru do zmiennej: LD value,R Zastanów się jaki może wystąpić przeplot tych operacji atomowych podczas wykonywania metody inc() przez 2 procesy. Odpowiedź znajduje się na następnej stronie. 3 Niekorzystny przeplot operacji /∗ ∗ p r z y k l a d na z w i e k s z a n i e o 1 p r z e z 2 p r o c e s y (P0 , P1) r o w n o c z e s n i e ∗ P0 : LD R, x z a l a d u j x do r e j e s t r u P0 : INC R zwieksz rejestr P1 : LD R, x P1 : INC R P0 : ST R, x z a p i s z x do r e j e s t r u P1 : ST R, x ∗ ∗/ Problem równoczesnego dostępu do metody można rozwiązać poprzez użycie słowa synchronized w deklaracji metody. Poczytaj nt. synchronized i spróbuj naprawić licznik. 2 Sekcje krytyczne wewnątrz metody Przykład: public c l a s s AdvancedCounter { private int v a l u e 1 = 0 ; private int v a l u e 2 = 0 ; private Object l o c k 1 = new Object ( ) ; private Object l o c k 2 = new Object ( ) ; public void i n c 1 ( ) { synchronized ( l o c k 1 ) { v a l u e 1 ++; } } public void i n c 2 ( ) { synchronized ( l o c k 2 ) { v a l u e 2 ++; } } public int g e t 1 ( ) { return v a l u e 1 ; } public int g e t 2 ( ) { return v a l u e 2 ; 4 } } 3 Sekcje krytyczne - piaskownica Poeksperymentuj z poniższym programem używając słowa kluczowego synchronized zarówno dla metod jak i bloków kodu. c l a s s Shared { public void f o o ( S t r i n g threadName , long time ) { System . out . p r i n t l n ( threadName + " ␣ i s ␣ r u n n i n g ␣ f o o ( ) ␣ f o r ␣ "+time+" ␣ms , ␣ c t : " + System . c u r r e n t T i m e M i l l i s ( ) ) ; long t = System . c u r r e n t T i m e M i l l i s ( ) ; while ( System . c u r r e n t T i m e M i l l i s ()− t < time ) { Math . atan ( System . c u r r e n t T i m e M i l l i s ( ) ) ; } } public void bar ( S t r i n g threadName , long time ) { System . out . p r i n t l n ( threadName + " ␣ i s ␣ r u n n i n g ␣ bar ( ) ␣ f o r ␣ "+time+" ␣ms , ␣ c t : " + System . c u r r e n t T i m e M i l l i s ( ) ) ; long t = System . c u r r e n t T i m e M i l l i s ( ) ; while ( System . c u r r e n t T i m e M i l l i s ()− t < time ) { Math . atan ( System . c u r r e n t T i m e M i l l i s ( ) ) ; } } } c l a s s ThreadA extends Thread { Shared s h a r e d ; ThreadA ( Shared i n s t a n c e ) { this . shared = instance ; } public void run ( ) { s h a r e d . f o o ( "A" , 5 0 0 0 ) ; s h a r e d . bar ( "A" , 1 0 0 0 ) ; System . out . p r i n t l n ( "A␣ i s ␣ done " ) ; } } c l a s s ThreadB extends Thread { Shared s h a r e d ; 5 ThreadB ( Shared i n s t a n c e ) { this . shared = instance ; } public void run ( ) { s h a r e d . f o o ( "B" , 1 0 0 0 ) ; s h a r e d . bar ( "B" , 2 0 0 0 ) ; System . out . p r i n t l n ( "B␣ i s ␣ done " ) ; } } public c l a s s J a v a A p p l i c a t i o n 1 { /∗ ∗ ∗ @param a r g s t h e command l i n e arguments ∗/ public s t a t i c void main ( S t r i n g [ ] a r g s ) { // TODO code a p p l i c a t i o n l o g i c h e r e Shared s h a r e d = new Shared ( ) ; ThreadA th1 = new ThreadA ( s h a r e d ) ; ThreadB th2 = new ThreadB ( s h a r e d ) ; long t = System . c u r r e n t T i m e M i l l i s ( ) ; th1 . s t a r t ( ) ; th2 . s t a r t ( ) ; try { th1 . j o i n ( ) ; th2 . j o i n ( ) ; } catch ( I n t e r r u p t e d E x c e p t i o n e ) { System . out . p r i n t l n ( e ) ; } System . out . p r i n t l n ( " done ␣ i n ␣ " + ( System . c u r r e n t T i m e M i l l i s ( ) − t ) + "␣ms" } } 4 Zakleszczenie Dlaczego dochodzi do zakleszczenia? public c l a s s DeadlockDemo1 { static class Friend { private f i n a l S t r i n g name ; 6 public F r i e n d ( S t r i n g name ) { t h i s . name = name ; } public S t r i n g getName ( ) { return t h i s . name ; } public synchronized void bow ( F r i e n d bower ) { System . out . format ( "%s : ␣%s ␣ has ␣bowed␣ t o ␣me!%n" , t h i s . name , bower . getName ( ) ) ; bower . bowBack ( t h i s ) ; } public synchronized void bowBack ( F r i e n d bower ) { System . out . format ( "%s : ␣%s ␣ has ␣bowed␣ back ␣ t o ␣me!%n" , t h i s . name , bower . getName ( ) ) ; } } public s t a t i c void main ( S t r i n g [ ] a r g s ) { f i n a l F r i e n d a l p h o n s e = new F r i e n d ( " Alphonse " ) ; f i n a l F r i e n d g a s t o n = new F r i e n d ( " Gaston " ) ; new Thread (new Runnable ( ) { public void run ( ) { a l p h o n s e . bow ( g a s t o n ) ; } }). start (); new Thread (new Runnable ( ) { public void run ( ) { g a s t o n . bow ( a l p h o n s e ) ; } }). start (); } } 5 Zadania Na razie nic.... 7