0% au considerat acest document util (0 voturi)
12 vizualizări6 pagini

Definiţie: Un Segment de Cod Ce Gestionează o Resursă Comună Mai Multor Fire de

Documentul prezintă metode de sincronizare a thread-urilor în Java, inclusiv monitoare (utilizând cuvântul cheie synchronized), metodele wait(), notify() și notifyAll(). De asemenea, prezintă un exemplu de implementare a unei clase Producer și Consumer care utilizează o clasă CubbyHole pentru a sincroniza accesul la o resursă comună.

Încărcat de

Andrii Andrei
Drepturi de autor
© All Rights Reserved
Respectăm cu strictețe drepturile privind conținutul. Dacă suspectați că acesta este conținutul dumneavoastră, reclamați-l aici.
Formate disponibile
Descărcați ca DOCX, PDF, TXT sau citiți online pe Scribd
0% au considerat acest document util (0 voturi)
12 vizualizări6 pagini

Definiţie: Un Segment de Cod Ce Gestionează o Resursă Comună Mai Multor Fire de

Documentul prezintă metode de sincronizare a thread-urilor în Java, inclusiv monitoare (utilizând cuvântul cheie synchronized), metodele wait(), notify() și notifyAll(). De asemenea, prezintă un exemplu de implementare a unei clase Producer și Consumer care utilizează o clasă CubbyHole pentru a sincroniza accesul la o resursă comună.

Încărcat de

Andrii Andrei
Drepturi de autor
© All Rights Reserved
Respectăm cu strictețe drepturile privind conținutul. Dacă suspectați că acesta este conținutul dumneavoastră, reclamați-l aici.
Formate disponibile
Descărcați ca DOCX, PDF, TXT sau citiți online pe Scribd

CAPITOLUL 1

1.1. Metode de sincronizare a thread-urilor

1.1.1 Monitoare (cuvântul cheie synchronized)

Definiţie: Un segment de cod ce gestionează o resursă comună mai multor fire de


execuţie separate şi concurente se numeşte secţiune critică. În Java o secţiune critică poate fi
un bloc de instrucţiuni sau o metodă.
Controlul accesului într-o secţiune critică se face prin cuvântul cheie synchronized.
Platforma Java asociază un monitor fiecărui obiect al unui program ce conţine secţiuni critice
care necesită sincronizare. Acest monitor va indica dacă resursa critică este accesată de vreun fir
de execuţie sau este liberă, cu alte cuvinte "monitorizează" o resursă critică. În cazul în care este
accesată, va "pune un lacăt" pe aceasta, astfel încât să împiedice accesul altor fire de execuţie la
ea. În momentul când resursa este eliberată "lacătul" va fi eliminat pentru a permite accesul altor
fire de execuţie.
În exemplul tip producător/consumator de mai sus, secţiunile critice sunt metodele put şi
get, iar resursa citică comună este obiectul buffer. Consumatorul nu trebuie să acceseze
buffer-ul când producătorul tocmai pune o valoare în el, iar producătorul nu trebuie sa modifice
valoarea din buffer în momentul când aceasta este citită de către consumator.
public synchronized int get() {
...
}
public synchronized void put(int number) {
...
}
Ambele metode au fost declarate cu modificatorul synchronized. Cu toate acestea
sistemul asociază un monitor unei instanţe a clasei Buffer şi nu unei metode anume. În
momentul în care se preiamonitorul, firul de execuţie care a făcut apelul, va bloca obiectul a
cărui metodă o accesează, ceea ce înseamnă că celelalte fire de execuţie nu vor mai putea accesa
resursele critice. Deoarece mai multe secţiuni critice (metode sincrone) ale unui obiect
gestioneazăo singură resursă critică. În exemplul nostru, atunci când producătorul apelează
metoda put pentru a scrie un număr, va bloca tot obiectul de tip Buffer, prin urmare firul de
execuţie consumator nu va avea acces la cealaltă metodă sincronă get, şi reciproc.
public synchronized void put(int number) {
// buffer blocat de producător
...
// buffer deblocat de producător
}
public synchronized int get() {
// buffer blocat de consumator
...
// buffer deblocat de consumator
}
1.1.2 Metodele wait, notify şi notifyAll
Obiectul de tip Buffer din exemplu de mai sus are o variabilă membră privată numită
number, în care este memorat numărul pe care îl comunică producătorul şi din care îl preia
consumatorul. Variabilă privată logică available,care indică starea buffer-ului: dacă are
valoarea true înseamnă că producătorul a plasat o valoare în buffer şi consumatorul nu a
preluat-o încă; dacă este false, consumatorul a preluat valoarea din buffer, dar producătorul nu
a plasat alta în buffer. Metodele clasei Buffer sunt:
public synchronized int get() {
if (available) {
available = false;
return number;
}
}
public synchronized int put(int number) {
if (!available) {
available = true;
[Link] = number;
}
}
Realizarea acestor metode nu va genera un rezultat corect, deoarece firele de execuţie, nu
sincronizează accesul la buffer. Situaţiile în care metodele get şi put nu fac nimic, vor duce la
pierdereaunor numere de către consumator, sau preluarea aceluieși număr de două ori. Aşadar,
cele două fire de execuţie trebuie să se aştepte unul pe celălalt.
public synchronized int get() {
while (!available) {
//nimic - aştept ca variabila să devină true
}
available = false;
return number;
}
public synchronized int put(int number) {
while (available) {
//nimic - aştept ca variabila să devină false
}
available = true;
[Link] = number;
}
Programul nu este corect, deoarece cele două metode îşi aşteaptă în mod ”egoist” condiţia
de terminare. Ca urmare, corectitudinea funcţionării va depinde de sistemul de operare, ceea ce
reprezintă o greşeală de programare. Punerea corectă a unui fir de execuţie în aşteptare se
realizează cu metoda wait a clasei Thread, care are trei forme:
void wait( )
void wait( long timeout )
void wait( long timeout, long nanos ).
După apelul metodei wait, firul de execuţie curent eliberează monitorul asociat
obiectului respectiv şi aşteaptă ca una din următoarele condiţii să fie îndeplinită:
 un alt fir de execuţie informează pe cei care "aşteaptă" la un anumit monitor să se
"trezească"; acest lucru se realizează printr-un apel al metodei notifyAll sau
notify.
 perioada de aşteptare specificată a expirat.
Metoda wait poate produce excepţii de tipul InterruptedException, atunci când
firul de execuţie este în starea de așteptare -Not Runnable este întrerupt din aşteptare şi trecut
forţat în starea Runnable. Metoda notifyAll informează toate firele de execuţie, care sunt
în aşteptare la monitorul obiectului curent, îndeplinirea condiţiei pe care o aşteptau. Metoda
notify informează doar un singur fir de execuţ[Link] corectă a metodelor get şi
puteste:
public synchronized int get() {
while (!available) {
try {
wait();
//aşteaptă producătorul să pună o valoare
} catch (InterruptedException e) { }
}
available = false;
notifyAll();
return number;
}

public synchronized void put(int number) {


while (available) {
try {
wait(); //aşteaptă
consumatorul să preia valoarea
} catch (InterruptedException e) { }
}
[Link] = number;
available = true;
notifyAll();
}}

Lucrare de laborator nr. 1

1. Tema lucrării:
Implementarea metodelor de sincronizare a proceselor.
2. Scopul lucrării:
Însuşirea metodelor de sincronizare a thread-urilor

3. Etapele de realizare:
1) Utilizarea clasei Threadpentru crearea unei clase noi;
2) Utilizarea interfeţei Runnable pentru crearea şi lansarea de thread-uri;
3) Implementarea clasei producător;
4) Implementarea clasei consummator;
5) Sincronizarea thread-urilor prin diferite metode:
6) a) utilizând variabile logice;
b) utilizând monitoare (synchronized).

4. Exemplu de realizare:

public class Producer extends Thread {


private CubbyHole cubbyhole;
private int number;

public Producer(CubbyHole c, int number) {


cubbyhole = c;
[Link] = number;
}
public void run() {
for (int i = 0; i < 10; i++) {
[Link](i);
[Link]("Producer #" +
[Link] + " put: " + i);
try {
sleep((int)([Link]() * 100));
} catch (InterruptedException e) { }
}
}
}
public class Consumer extends Thread {
private CubbyHole cubbyhole;
private int number;
public Consumer(CubbyHole c, int number) {
cubbyhole = c;
[Link] = number;
}
public void run() {
int value = 0;
for (int i = 0; i < 10; i++) {
value = [Link]();
[Link]("Consumer #" +
[Link] + " got: " + value);
}
}
}
public class ProducerConsumerTest {
public static void main(String[] args) {
CubbyHole c = new CubbyHole();
Producer p1 = new Producer(c, 1);
Consumer c1 = new Consumer(c, 1);
[Link]();
[Link]();
}
}
class CubbyHole {
private int number = -1;
private boolean available = false;
public synchronized int get() {
while (!available) {
try {
wait();
//aşteaptă producatorul să pună o valoare
} catch (InterruptedException e) { }
}
available = false;
notifyAll();
return number;
}
public synchronized void put(int number) {
while (available) {
try {
wait();
//aşteaptă consumatorul să preia valoarea
} catch (InterruptedException e) { }
}
[Link] = number;
available = true;
notifyAll();
}
}

5. Probleme propuse spre realizare:


X producători generează aleatoriu F obiecte care sunt consumate de Y consumatori. De
afişat informaţia despre producerea şi consumarea obiectelor, mesajele despre cazurile când
“depozitul e gol sau plin”. Toate operaţiile se efectuează până când producătorii nu vor produce
Z obiecte și consumatorii nu vor consuma aceste obiecte.
Dimensiunea depozitului este D. Valorile pentru X, Y, Z, D sunt indicate în Tabelul 3.

Tabelul 3 Variantele pentru realizarea sarcinii


Nr X Y Z D Tip Obiecte
.
1 2 3 49 8 Numere pare
2 3 4 19 5 Numere impare
3 4 3 31 10 Vocale
4 3 2 38 11 Consoane
5 2 5 51 12 Numere pare
6 2 4 47 7 Numere impare
7 3 3 53 6 Vocale
8 4 2 43 5 Consoane
9 5 2 37 4 Numere pare
10 3 3 57 2 Numere impare
* fiecare producător produce câte 1 obiecte de fiecare dată

6. Întrebări de verificare:

1. Pentru ce se folosește sincronizarea firelor de execuție în Java?


2. Pentru ce se folosește clasa BUFFER și care sunt metodele acesteia?
3. Pentru ce se folosesc metodele: wait, notify, și notifyAll, cărei clase aparțin
acestea?
4. Pentru ce se folosește modificatorul synchronized?
5. Pentru ce se folosește mecanismul barieră?

S-ar putea să vă placă și