• Nie Znaleziono Wyników

Systemy Rozproszone - Ćwiczenie 2

N/A
N/A
Protected

Academic year: 2021

Share "Systemy Rozproszone - Ćwiczenie 2"

Copied!
7
0
0

Pełen tekst

(1)

Systemy Rozproszone - Ćwiczenie 2

1 Tworzenie wątku przez dziedziczenie po klasie Thread

Pierwszy sposób tworzenia wątku polega na stworzeniu klasy potomnej, dziedzi- czącej po klasie Thread. Zachowanie wątku określa się implementując metodę run() w klasie wątku. Wątek, po uprzednim stworzeniu, uruchamia się wywołu- jąc jego metodę start(). W konsekwencji system zacznie wykonywanie metody run() wątku.

public c l a s s HelloWorldThread extends Thread { public void run ( ) {

System . ou t . p r i n t l n ( " H e l l o ␣ World " ) ; }

/∗ ∗

∗ Program d e m o n s t r u j e s p o s o b t w o r z e n i a i

∗ uruchomiania p o j e d y n c z e g o watku

∗ @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 ) {

HelloWorldThread t h r e a d = new HelloWorldThread ( ) ; t h r e a d . s t a r t ( ) ;

} }

2 Przydzielanie wątkom czasu procesora

Poniższy program demonstruje dostęp do wspólnego zasobu (konsoli) równolegle przez 2 wątki i glówny program. W main() tworzone i uruchamiane są 2 wątki, o nazwach "A" i "B". Każdy z wątków działa tak, że w pętli for wypisuje na standardowe wyjscie swoją nazwę. Dodatkowo metoda main() również wypisuje na standardow wyjście swoją nazwę ("M"). Zobacz w jaki sposób wątki i głów- ny program rywalizują o dostęp do konsoli. Jak intensywny jest przeplot tego dostępu? Czy tego się spodziewałes?

public c l a s s HelloWorldThreadExt extends Thread {

(2)

private S t r i n g threadName ;

public HelloWorldThreadExt ( S t r i n g name ) { t h i s . threadName = name ;

}

public void run ( ) {

f o r ( i n t i =0; i <1000; i ++) {

System . o u t . p r i n t ( t h i s . threadName ) ; }

} /∗ ∗

∗ Program d e m o n s t r u j e u r u c h o m i e n i e 2 r o w n o l e g l y c h watkow i g l o w n e g o programu

∗/

public s t a t i c void main ( S t r i n g [ ] a r g s ) throws I n t e r r u p t e d E x c e p t i o n { HelloWorldThreadExt t h r e a d 1 = new HelloWorldThreadExt ( "A" ) ;

HelloWorldThreadExt t h r e a d 2 = new HelloWorldThreadExt ( "B" ) ; t h r e a d 1 . s t a r t ( ) ;

t h r e a d 2 . s t a r t ( ) ;

f o r ( i n t i =0; i <1000; i ++) { System . o u t . p r i n t ( "M" ) ; }

}

3 Tworzenie wątku przez implementację interfej- su Runnable

Innym sposobem tworzenia wątku jest implementacja interfejsu Runnable. W poniższym przykładzie tworzymy klasę HelloWorldRunnable, która dziedziczy po klasie Foo. Dodatkowo, klasa HelloWorldRunnable implementuje interfejs Runnable. Klasa implementująca ten interfejs musi zawierać implementację me- tody run(). Zwróć uwagę na sposób tworzenia wątku.

c l a s s Foo {

private S t r i n g name ; public Foo ( S t r i n g name ) {

t h i s . name = name ; }

public S t r i n g get_name ( ) { return t h i s . name ;

(3)

}

public c l a s s HelloWorldRunnable extends Foo implements Runnable { public HelloWorldRunnable ( S t r i n g name ) {

super ( name ) ; }

public void run ( ) {

System . ou t . p r i n t l n ( " Thread ␣ "+t h i s . get_name ()+ " ␣ i s ␣ r u n n i n g " ) ; }

/∗ ∗

∗ Program d e m o n s t r u j e u t w o r z e n i e k l a s y z i n t e r f e j s e m Runnable

∗ (w c e l u u m o z l i w i e n i a d z i e d z i c z e n i a ) i u r u c h o m i e n i e t a k i e g o o b i e k t u

∗ j a k o watku

∗/

public s t a t i c void main ( S t r i n g [ ] a r g s ) throws I n t e r r u p t e d E x c e p t i o n { HelloWorldRunnable r u n n a b l e = new HelloWorldRunnable ( " r u n n a b l e " ) ; Thread t h r e a d = new Thread ( r u n n a b l e ) ;

t h r e a d . s t a r t ( ) ; }

}

4 Synchronizacja wątków - join()

Wywołanie metoda x.join() wątku x, pozwala na wstrzymanie wykonywania bieżącego wątku do czasu aż wątek x sie zakończy. Ilustruje to poniższy przy- kład, w którym wątek główny tworzy 2 wątki poboczne - zadaniem pierwszego jest policzenie sumy elementów nieparzystych w tablicy, zadaniem drugiego jest zliczenie elementów nieparzystych w tablicy. Wątek główny uruchamia 2 wąt- ki poboczne i czeka aż skończą pracę. Następnie oblicza średnią na podstawie wyników obliczonych przez wątki poboczne.

c l a s s OddCountThread extends Thread { protected i n t [ ] numbers ;

protected i n t r e s u l t =0;

public OddCountThread ( i n t [ ] numbers ) { t h i s . numbers = numbers ;

}

public boolean is_odd ( i n t e l e m e n t ) { return e l e m e n t % 2 == 1 ;

}

(4)

public void run ( ) {

f o r ( i n t e l e m e n t : numbers ) { i f ( is_odd ( e l e m e n t ) )

r e s u l t ++;

} }

public i n t g e t _ r e s u l t ( ) { return r e s u l t ;

} }

c l a s s OddSumThread extends OddCountThread { public OddSumThread ( i n t [ ] numbers ) {

super ( numbers ) ; }

@Override

public void run ( ) {

f o r ( i n t e l e m e n t : numbers ) { i f ( is_odd ( e l e m e n t ) )

r e s u l t+=e l e m e n t ; }

} }

public c l a s s ThreadedAverage {

public s t a t i c void main ( S t r i n g [ ] a r g s ) {

i n t [ ] numbers = { 1 , 8 , 5 , 3 , 2 , 5 , 9 9 , 6 } ;

OddCountThread count_job = new OddCountThread ( numbers ) ; OddSumThread sum_job = new OddSumThread ( numbers ) ;

sum_job . s t a r t ( ) ; count_job . s t a r t ( ) ; try {

sum_job . j o i n ( ) ; count_job . j o i n ( ) ;

f l o a t oddAverage = ( f l o a t ) sum_job . g e t _ r e s u l t ( ) / ( f l o a t ) count_job . g e t _ r e s u l t ( ) ; System . o u t . p r i n t l n ( " S r e d n i a ␣ z ␣ elementow ␣ n i e p a r z y s t y c h ␣ t o ␣ " + oddAverage ) ;

}

catch ( I n t e r r u p t e d E x c e p t i o n e ) { System . e r r . p r i n t l n ( e ) ;

(5)

} }

Następny przykład jest trochę bardziej skomplikowany. Przeanalizuj jego działanie.

public c l a s s CalcSum extends Thread { i n t [ ] d a t a ;

i n t f i r s t , l a s t ; i n t r e s u l t ;

public i n t g e t _ r e s u l t ( ) { return t h i s . r e s u l t ; }

public CalcSum ( i n t [ ] d a t a ) { t h i s . r e s u l t = 0 ;

t h i s . d a t a = d a t a ; t h i s . f i r s t = 0 ;

t h i s . l a s t = d a t a . l e n g t h −1;

}

public CalcSum ( i n t [ ] data , i n t f i r s t , i n t l a s t ) { t h i s . r e s u l t = 0 ;

t h i s . d a t a = d a t a ; t h i s . f i r s t = f i r s t ; t h i s . l a s t = l a s t ; }

public void run ( ) { i f ( f i r s t==l a s t ) {

r e s u l t = d a t a [ f i r s t ] ; }

e l s e {

i n t m i d d l e = ( f i r s t +l a s t ) / 2 ;

CalcSum c1 = new CalcSum ( data , f i r s t , m i d d l e ) ; CalcSum c2 = new CalcSum ( data , m i d d l e +1 , l a s t ) ; c1 . s t a r t ( ) ;

c2 . s t a r t ( ) ; try {

c1 . j o i n ( ) ; c2 . 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 ) { } ;

r e s u l t = c1 . g e t _ r e s u l t ( ) + c2 . g e t _ r e s u l t ( ) ; }

}

(6)

/∗ ∗

∗ Program o b l i c z a sume elementow w t a b l i c y w s p o s o b r e k u r e n c y j n y ,

∗ p r z y pomocy watkow .

∗ Program d e m o n s t r u j e w j a k i s p o s o b w a t e k moze c z e k a c na z a k o n c z e n i e i n n y c h

∗ watkow .

∗/

public s t a t i c void main ( S t r i n g [ ] a r g s ) {

i n t [ ] d a t a = { 1 , 1 , 2 , 6 , 2 4 , 1 2 0 , 7 2 0 , 5 0 4 0 } ; CalcSum c = new CalcSum ( d a t a ) ;

c . s t a r t ( ) ;

// t o z a d z i a l a n i e p o p r a w n i e

System . ou t . p r i n t l n ( "Sum␣ i s ␣ " + c . g e t _ r e s u l t ( ) ) ; // t o z a d z i a l a p o p r a w n i e

try {

c . 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 . ou t . p r i n t l n ( "Sum␣ i s ␣ " + c . g e t _ r e s u l t ( ) ) ; }

}

5 Zadania

5.1 Suma wielkiej tablicy

Zmodyfikuj poniższy program tak, żeby suma tangensów była liczona w sposób wielowątkowy. Zmień program tak, żeby łatwo można było zmienić liczbę wąt- ków użytych do obliczenia sumy. Sprawdź eksperymentalnie jaka jest zależność między liczbą wątków a czasem wykonania programu. Przeprowadź ekspery- menty dla tablic o różnych wielkościach (100, 10000, 1000000, ...) i dla różnej wielkości wątków (1,2,4,8,16,32).

public c l a s s SumOfTangents {

public s t a t i c void main ( S t r i n g [ ] a r g s ) {

double [ ] a r r a y = new double [ 1 0 0 ∗ 1 0 0 0 0 0 0 ] ; Random r = new Random ( ) ;

f o r ( i n t i =0; i <a r r a y . l e n g t h ; i ++) { a r r a y [ i ] = r . n e x t D o u b l e ( ) ;

}

long s t a r t T i m e = System . c u r r e n t T i m e M i l l i s ( ) ;

(7)

double t o t a l = 0 ;

f o r ( i n t i =0; i <a r r a y . l e n g t h ; i ++) { t o t a l += Math . t a n ( a r r a y [ i ] ) ; }

long stopTime = System . c u r r e n t T i m e M i l l i s ( ) ; System . ou t . p r i n t l n ( " T o t a l ␣ i s : ␣ " + t o t a l ) ;

System . ou t . p r i n t l n ( " E l a p s e d ␣ t i m e : ␣ " + ( stopTime − s t a r t T i m e ) + " ␣ m i l i s e c o n d s . " ) ; }

}

5.2 Wyścig wątków

Załóżmy, że stworzymy np. 10 wątków, z których każdy wykonywać bedzie te same obliczenia. Czy wątki te zakończą obliczenia w takiej samej kolejności jak zostały uruchomione? Czy tak kolejność bedzie taka sama za każdym urucho- mieniem programu? Sprawdźmy to eksperymentalnie. Stwórz klase Zawodnik, która ma być wątkiem wykonującym pewne obliczenia (np. zapełnij 1000 ele- mentową tablice klasy Zawodnik kolejnymi wartosciam funcji tangens). Stwórz klase Wyscig, która stworzy i uruchomi wątki klasy Zawodnik. Każdy zawodnik po zakończeniu obliczeń powinien zgłosić ten fakt (np. wypisując komunikat na konsoli). Przeprowadź eksperyment dla różnej ilości wątków i różnego poziomu skomplikowania metody run(). Czy zauważasz jakieś prawidłowości? Rozbuduj program o klase Tablica wyników. Klasa wyścig powinna przydzielić każdemu za- wodnikowi numer, a po zakończeniu pracy przez zawodnika, powinien on zgłosić swój numer do tablicy wyników (wywołując odpowiednią metode klasy Tablica), a nastepnie tablica wypisze stosowna informacje na konsoli.

5.3 Sztafeta wątków

Zmodyfikujmy wyścigi w nastepujacy sposob. Wyścigi rozgrywane bedą w szta- fecie 10 zespołów po 4 wątki. Zawodnicy zostana podzieleni na zespoły (klasa Zespół), i to teraz zespoły, a nie zawodnicy, biorą udział w wyścigu. Każdy za- wodnik zna swój numer startowy i numer zespołu do której należy. Klasa Zespół ma być odpowiedzialna za uruchamianie kolejnych wątków należących do jedego zespołu ale w taki sposób, żeby tylko jeden zawodnik w zespole był uruchomiony jednocześnie (do tego celu należy użyć metody join()). Klasa zespół powinna informować o rozpoczeciu/zakończeniu wyścigu przez zawodnika i o zakończeniu sztafety przez ten zespół. Zmodyfikuj program tak, żeby to zawodnik czekał na zokończenie pracy przez poprzedniego zawodnika.

Cytaty

Powiązane dokumenty

Jeśli macie ochotę, wyślijcie mi zdjęcie uzupełnionej krzyżówki lub po prostu hasło krzyżówki, jako wynik swojej pracy (beata.bozejewicz@gmail.com lub

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

Metoda ta obsługuje żądania od klientów w pętli while, w której to serwer blo- kuje się do czasu połączenia z klienem (metoda ServerSocket.accept()), a następnie po

Pierwszym krokiem przy tworzeniu obiektu zdalnego jest określenie jego interfej- su, który dziedziczy po klasie java.rmi.Remote.

Najbardziej produktywnym Wydziałem Uniwersytetu Jagiellońskiego pod względem projektów o charakterze wdrożeniowym jest Wydział Chemii. W 2009 roku aż 8 z 15 zgłoszonych do CITTRU

 The model is based on the observation that most of the time a user does not need any computer power but once in a while he may need a very large amount of computing power for a

Wraz z szybkim rozwojem technologii infor- matycznych (między innymi zwiększeniem szybkości sieci, wzrostem wydajności kom- puterów) pojawiła się potrzeba opracowania

W przypadku ciał skończonych charakterystyka ciała jest liczbą pierwszą, a ciało rozsze- rzone zachowują charakterystykę ciała prostego, nad którym zostało