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