33B Skrzypek Mateusz LAB 5


Przetwarzanie współbieżne
Grupa: Data lab:
Autor: Mateusz Skrzypek
5
Czwartek, 14:30 27 III 2014
Cele laboratorium:
vð Opanowanie podstawowych metod synchronizacji w Javie
Wstęp:
Czym sÄ… wÄ…tki:
Tworząc swoje aplikacje, szczególnie te wykorzystujące interfejs użytkownika w której
pewna czynność, jak na przykład obliczenie wyniku skomplikowanej funkcji, czy pobranie
pewnych danych z bazy danych, zabierało dużo czasu, a przez to aplikacja sprawiała wrażenie
jakby się zawiesiła. Nie jest to pożądana funkcjonalność i każdy programista chciałby jej
uniknąć.
Istnieje jednak rozwiązanie, które w stosunkowo prosty sposób pozwala poradzić sobie z tym
problemem  sÄ… to wÄ…tki.
Wątki pozwalają na symultaniczne wykonywanie pewnych operacji dzięki czemu czas
wykonania pewnych operacji można znacząco skrócić. W przypadku przykładu z
 zawieszeniem się interfejsu użytkownika można pewne skomplikowane obliczenia
wykonać asynchronicznie w tle, dzięki czemu użytkownik aplikacji będzie miał lepsze
odczucia w związku z jej użytkowaniem. Na obrazku wygląda to tak:
To co istotne to fakt, że zastosowanie
wątków sprawdza się nawet na
procesorze, który posiada tylko
jeden rdzeń. Jest to spowodowane tym,
że każdy z wątków może otrzymywać
swój czas procesora na
wykonanie pewnych operacji. W
przypadku jednego wÄ…tku nie ma
wyjścia  jeśli wchodzimy do funkcji
obliczającej skomplikowane równanie,
cały interfejs użytkownika jest
zamrażany aż do momentu
skończenia obliczeń, natomiast jeśli
obliczenia uruchomimy w wÄ…tku
niezależnym od interfejsu będą
one naprzemiennie otrzymywały krótki
czas procesora i będą sprawiały
wrażenie wykonywania się
równoległego (a w przypadku procesora wielordzeniowego faktycznie tak będą działały) 
oczywiście pomijamy tu fakt tego, że obok nich wykonuje się wiele innych procesów, które
również walczą o uzyskanie czasu procesora. Bardzo ważne jest również to, że jeśli
uruchomimy jeden po drugim np.
10 wątków, to wcale nie będą one wykonywały się sekwencyjnie jeden po drugim, jeśli sami
o to nie zadbamy. Jeśli jakieś zadanie nie zdąży się wykonać w czasie, który został dla niego
przydzielony, to
może ono zostać przerwane na pewien czas.
Kiedy wykorzystywać wątki? W wielu sytuacjach:
vð Wszelkie obliczenia, które mogÄ… zablokować interfejs użytkownika powinny być
wykonywane asynchronicznie,
vð Animacje, które powinny być przetwarzane niezależnie od interfejsu użytkownika
vð Pobieranie danych z Internetu (zamiast przetwarzać strony internetowe jedna po drugiej
można połączyć się np. z 10 jednocześnie)
vð W ogólnoÅ›ci wszystkie operacje wejÅ›cia/wyjÅ›cia, zapis i odczyt plików, czy baz danych
vð ZÅ‚ożone obliczenia, które mogÄ… być podzielone na mniejsze podzadania
vð I wiele innych
Realizacja zadania:
Kod programu 1 (zawartość Pub, main oraz Klient):
package pub;
/**
* @author mskrzyp
*/
import java.util.logging.Level;
import java.util.logging.Logger;
/////////////////////////////////////////////////////// Pub
/////////////////////////////////////////////////////////
public class Pub {
int[] tacka_kufli; //kufle
int ilosc_klientow, ilosc_kufli;
// Konstruktor sparametryzowany Pub
public Pub(int ilosc_klientow, int ilosc_kufli) {
this.ilosc_kufli = ilosc_kufli;
this.ilosc_klientow = ilosc_klientow;
tacka_kufli = new int[this.ilosc_kufli];
// Na poczÄ…tku wszystkie kufle sÄ… puste
for (int i = 0; i < this.ilosc_kufli; i++) {
tacka_kufli[i] = 1;
}
}
public synchronized void zabierzKufel(Klient klient) {
System.out.println("Klient nr. " + klient.nr_klienta + " czeka na
kufel");
while (true) {
try {
// Sprawdzamy wszystkie kufle
for (int indeks = 0; indeks < ilosc_kufli; indeks++) {
// Jeśli dany kufel jest wolny
if (tacka_kufli[indeks] == 1) {
tacka_kufli[indeks] = 0;
klient.nr_zabranego_kufla = indeks;
break;
}
}
// Jeśli nie ma pustego kufla
if (klient.nr_zabranego_kufla == -1) {
wait(); // Oczekujemy na kufel
} else {
break;
}
} catch (InterruptedException ex) {
Logger.getLogger(Pub.class.getName()).log(Level.SEVERE,
null, ex);
System.out.println("Wyjatek: wait - zabierzKufel");
}
}
System.out.println("Klient nr. " + klient.nr_klienta + " zabral
kufel nr. " + (klient.nr_zabranego_kufla + 1));
}
public synchronized void napelnijKufel(Klient klient) {
try {
System.out.println("Klient nr. " + klient.nr_klienta + "
nalewa piwo do kufla nr. "
+ (klient.nr_zabranego_kufla + 1));
Thread.sleep(1000);
System.out.println("Klient nr. " + klient.nr_klienta + " nalal
piwo do kufla nr. "
+ (klient.nr_zabranego_kufla + 1));
} catch (InterruptedException ie) {
System.out.println("Wyjatek");
}
}
public synchronized void zwrocPustyKufel(Klient klient) {
System.out.println("Klient nr. " + klient.nr_klienta + " odklada
kufel nr. " + (klient.nr_zabranego_kufla + 1));
tacka_kufli[klient.nr_zabranego_kufla] = 1; //oddajemy kufel
klient.nr_zabranego_kufla = -1; //klient nie posiada kufla
notifyAll(); //budzimy wszystkich oczekujacych klientow
}
/////////////////////////////////////////////////////// main
/////////////////////////////////////////////////////////
public static void main(String[] args) {
int ilosc_kufli = 1;
int ilosc_klientow = 2;
Pub pub = new Pub(ilosc_klientow, ilosc_kufli);
Klient[] klienci = new Klient[ilosc_klientow];
//tworzenie wątków - klientów
for (int i = 0; i < ilosc_klientow; i++) {
klienci[i] = new Klient(i + 1, pub);
}
//uruchamienie wątków
for (int i = 0; i < ilosc_klientow; i++) {
klienci[i].start();
}
}
}
/////////////////////////////////////////////////////// Klient
/////////////////////////////////////////////////////////
/**
* @author mskrzyp
*/
class Klient extends Thread {
Pub pub;
int nr_klienta;
int nr_zabranego_kufla;
//Konstruktor sparametryzowany
public Klient(int nr, Pub pub) {
this.nr_klienta = nr;
this.pub = pub;
nr_zabranego_kufla = -1;
}
@Override
public void run() {
//Każdy klient pije maksymalnie 2 piwa
for (int i = 1; i <= 2; i++) {
//Wez kufel
pub.zabierzKufel(this);
//Napełnij kufel
pub.napelnijKufel(this);
//Wypij pełny kufel
System.out.println("Klient nr. " + nr_klienta + " pije piwo w
kuflu nr. " + (nr_zabranego_kufla + 1));
//Oddaj pusty kufel
pub.zwrocPustyKufel(this);
}
}
}
Wynik programu 1:
Klient nr. 1 czeka na kufel
Klient nr. 1 zabral kufel nr. 1
Klient nr. 2 czeka na kufel
Klient nr. 1 nalewa piwo do kufla nr. 1
Klient nr. 1 nalal piwo do kufla nr. 1
Klient nr. 1 pije piwo w kuflu nr. 1
Klient nr. 1 odklada kufel nr. 1
Klient nr. 1 czeka na kufel
Klient nr. 1 zabral kufel nr. 1
Klient nr. 1 nalewa piwo do kufla nr. 1
Klient nr. 1 nalal piwo do kufla nr. 1
Klient nr. 1 pije piwo w kuflu nr. 1
Klient nr. 1 odklada kufel nr. 1
Klient nr. 2 zabral kufel nr. 1
Klient nr. 2 nalewa piwo do kufla nr. 1
Klient nr. 2 nalal piwo do kufla nr. 1
Klient nr. 2 pije piwo w kuflu nr. 1
Klient nr. 2 odklada kufel nr. 1
Klient nr. 2 czeka na kufel
Klient nr. 2 zabral kufel nr. 1
Klient nr. 2 nalewa piwo do kufla nr. 1
Klient nr. 2 nalal piwo do kufla nr. 1
Klient nr. 2 pije piwo w kuflu nr. 1
Klient nr. 2 odklada kufel nr. 1
Kod programu 2 (zawartość pub_trylock, main, Klient) :
package pub_trylock;
/**
* @author mskrzyp
*/
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/////////////////////////////////////////////////////// Pub_trylock
/////////////////////////////////////////////////////////
public class Pub_trylock {
int ilosc_klientow, ilosc_kufli;
int[] taca_kufli;
Lock kran = new ReentrantLock();
Lock lada = new ReentrantLock();
// Konstruktor sparametryzowany Pub_trylock
public Pub_trylock(int ilosc_klientow, int ilosc_kufli) {
this.ilosc_kufli = ilosc_kufli;
this.ilosc_klientow = ilosc_klientow;
taca_kufli = new int[this.ilosc_kufli];
// Na poczÄ…tku wszystkie kufle sÄ… puste
for (int i = 0; i < this.ilosc_kufli; i++) {
taca_kufli[i] = 1;
}
}
public void zabierzKufel(Klient klient) {
System.out.println("Klient nr. " + klient.nr_klienta + " czeka na
kufel");
while (true) {
// Sprawdzamy czy możemy dostać się do lady, żeby odebrać
kufel
if (lada.tryLock()) {
try {
// Sprawdzamy wszystkie kufle
for (int indeks = 0; indeks < ilosc_kufli; indeks++) {
// Jeśli dany kufel jest wolny
if (taca_kufli[indeks] == 1) {
taca_kufli[indeks] = 0;
klient.nr_zabranego_kufla = indeks;
break;
}
}
if (klient.nr_zabranego_kufla != -1) {
break;
}
} finally {
// Odebraliśmy kufel -> zwalniamy ladę dla innych
klientów
lada.unlock();
}
}
}
System.out.println("Klient nr. " + klient.nr_klienta + " zabral
kufel nr. " + (klient.nr_zabranego_kufla + 1));
}
public void napelnijKufel(Klient klient) {
while (true) {
// Sprawdzamy czy kran jest wolny, aby napełnić kufel
if (kran.tryLock()) {
try {
System.out.println("Klient nr. " + klient.nr_klienta +
" nalewa piwo do kufla nr. "
+ (klient.nr_zabranego_kufla + 1));
Thread.sleep(1000);
System.out.println("Klient nr. " + klient.nr_klienta +
" nalal piwo do kufla nr. "
+ (klient.nr_zabranego_kufla + 1));
break;
} catch (InterruptedException ie) {
System.out.println("Wyjatek: napelnijKufel sleep");
} finally {
// Po napełnieniu kufla zwalniamy kran
kran.unlock();
}
}
}
}
public void zwrocPustyKufel(Klient klient) {
System.out.println("Klient nr. " + klient.nr_klienta + " odklada
kufel nr. " + (klient.nr_zabranego_kufla + 1));
taca_kufli[klient.nr_zabranego_kufla] = 1; // oddajemy kufel
klient.nr_zabranego_kufla = -1; // klient nie posiada kufla
}
/////////////////////////////////////////////////////// main
/////////////////////////////////////////////////////////
public static void main(String[] args) {
int ilosc_kufli = 1;
int ilosc_klientow = 2;
Pub_trylock pub = new Pub_trylock(ilosc_klientow, ilosc_kufli);
Klient[] klienci = new Klient[ilosc_klientow];
//tworzenie wątków - klientów
for (int i = 0; i < ilosc_klientow; i++) {
klienci[i] = new Klient(i + 1, pub);
}
//uruchamiam watki
for (int i = 0; i < ilosc_klientow; i++) {
klienci[i].start();
}
}
}
/**
* @author mskrzyp
*/
/////////////////////////////////////////////////////// Klient
/////////////////////////////////////////////////////////
class Klient extends Thread {
Pub_trylock pub;
int nr_klienta;
int nr_zabranego_kufla;
// Konstruktor sparametryzowany
public Klient(int nr, Pub_trylock pub) {
this.nr_klienta = nr;
this.pub = pub;
nr_zabranego_kufla = -1;
}
@Override
public void run() {
//Każdy klient pije maksymalnie 2 piwa
for (int i = 1; i <= 2; i++) {
// Weż kufel
pub.zabierzKufel(this);
// Napełnij kufel
pub.napelnijKufel(this);
// Wypij pełny kufel
System.out.println("Klient nr. " + nr_klienta + " pije piwo w
kuflu nr. " + (nr_zabranego_kufla + 1));
// Oddaj pusty kufel
pub.zwrocPustyKufel(this);
}
}
}
Wynik programu 2:
Klient nr. 2 czeka na kufel
Klient nr. 1 czeka na kufel
Klient nr. 2 zabral kufel nr. 1
Klient nr. 2 nalewa piwo do kufla nr. 1
Klient nr. 2 nalal piwo do kufla nr. 1
Klient nr. 2 pije piwo w kuflu nr. 1
Klient nr. 2 odklada kufel nr. 1
Klient nr. 2 czeka na kufel
Klient nr. 1 zabral kufel nr. 1
Klient nr. 1 nalewa piwo do kufla nr. 1
Klient nr. 1 nalal piwo do kufla nr. 1
Klient nr. 1 pije piwo w kuflu nr. 1
Klient nr. 1 odklada kufel nr. 1
Klient nr. 2 zabral kufel nr. 1
Klient nr. 2 nalewa piwo do kufla nr. 1
Klient nr. 1 czeka na kufel
Klient nr. 2 nalal piwo do kufla nr. 1
Klient nr. 2 pije piwo w kuflu nr. 1
Klient nr. 2 odklada kufel nr. 1
Klient nr. 1 zabral kufel nr. 1
Klient nr. 1 nalewa piwo do kufla nr. 1
Klient nr. 1 nalal piwo do kufla nr. 1
Klient nr. 1 pije piwo w kuflu nr. 1
Klient nr. 1 odklada kufel nr. 1
Wnioski:
Wątki w Javie można tworzyć na kilka sposobów, poprzez:
vð jawne rozszerzenie klasy Thread (Program 1 i 2),
vð stworzenie klasy implementujÄ…cej interfejs Runnable, który może być wykonany w
osobnym wÄ…tku (Thread),
vð stworzenie klasy implementujÄ…cej interfejs Callable, który może być wykonany w
osobnym wÄ…tku (Thread).
Preferowane jest stosowanie rozszerzeń interfejsów (czyli 2 i 3 punkt), ponieważ dają one
dużo lepszą elastyczność, szczególnie jeśli dojdziemy do momentu szeregowania wątków,
utrzymywania stałej puli wątków wykonujących się w tle. Interfejsy Runnable i Callable są
do siebie bardzo podobne, jednak najważniejszą różnicą jest to, że Callable może zwrócić w
wyniku pewną wartość, natomiast w przypadku Runnable nie ma takiej możliwości.
W moich programach zastosowałem pierwszą możliwość czyli jawne rozszerzenie klasy
Thread. Wątki uruchamiam za pomocą metody start() która powoduje rozpoczęcie wykonania
kodu, który stworzyliśmy w metodzie run().
Wydruki programów udowadniają, że klienci nie będą pić piw po kolei w takiej kolejności jak
przyszli do pubu. Czyli pierwszy klient który będzie pić piwo może ale nie musi być
pierwszym klientem który przybył do baru. Może się okazać, że przy warunku opuszczenia
baru po wypiciu dwóch piw wyjdzie jako ostatni. Dzieje się tak za sprawą losowego
przypisywania czasu procesora różnym wątkom (klientom).
W drugim programie zastosowałem metodę TryLock. Zalety TryLock:
vð sÄ… bardziej efektywne od synchronized w sytuacji dużej konkurencji wÄ…tków o zasoby,
vð jako "zwykÅ‚e" obiekty Javy mogÄ… być dostÄ™pne przez referencje w wielu miejscach kodu
(np. przekazywane jako argumenty konstruktorów czy metod),
vð mogÄ… być zamykane i zwalniane w różnych strukturalnie sekcjach kodu np. w różnych
metodach (ale wykonywanych przez ten sam wątek), synchronized może być użyte tylko
w ramach tego samego bloku,
vð możliwe jest sprawdzenie, czy rygiel jest zamkniÄ™ty (inny wÄ…tek wykonuje sekcjÄ™
krytyczną) i np. wtedy zajęcie się innymi czynnościami, a nie blokowanie bieżącego
wątku na ryglu, mamy tu możliwości zastosowania metody tryLock() lub bezpośrednie
odpytywanie obiektu-rygla czy jest zamknięty,
vð możliwe jest oczekiwanie na uzyskanie dostÄ™pu do sekcji krytycznej (otwarcie rygla przez
inny wątek) przez określony czas (nie chcemy blokować bieżącego wątku zbyt długo),
służy do tego metoda tryLock(...) z podanym czasem oczekiwania,
vð możliwe jest przerwanie wÄ…tku zablokowanego na jakimÅ› ryglu, tu używamy metody
lockInterruptibly() (synchronized nie daje tej możliwości).
Podstawowy schemat działania:
Lock lock = new ReentrantLock(); // utworzenie rygla
// ....
// KOD WYKONYWANY WSPÓABIEŻNIE
lock.lock(); // zamknięcie rygla (1)
// ... kod sekcji krytycznej
lock.unlock(); // zwolnienie rygla (2)


Wyszukiwarka

Podobne podstrony:
33B Skrzypek Mateusz LAB 3 5
33B Kupczyk Wojciech LAB 3
lab 9 Krol Mateusz AUT2
spr lab nr 7 Suchocki Mateusz I1G1S4
Lab cpp
lab 2
T2 Skrypt do lab OU Rozdział 6 Wiercenie 3
IE RS lab 9 overview
lab pkm 3
M 8 Mateusz Wittstock
lab chemia korozja
lab tsp 3
Ewangelia Pseudo Mateusza
Lab

więcej podobnych podstron