Laborator 2


Recapitulare semafoare in Java

La modul general, informal, un semafor este o primitiva de sincronizare bazata pe un contor intern si doua operatii de baza pentru a oferi functionalitatea de sincronizare. Considerand contorul c, aceste doua operatii pot fi definite in forma lor cea mai simpla, in mod atomic, in urmatorul mod:

signal () {
	
    c++ ; 		
}
wait () {
	
    while (true) {
	if (c > 0) {
	    c--;
	    break;
	}
    }
}

Contorul are restrictia de a nu deveni negativ. Aceasta are ca rezultat un efect blocant de sincronizare in momentul in care atinge valoarea 0. Un thread ce va apela wait() va fi blocat si nu va fi capabil sa avanseze pana ce un alt thread va apela signal() incrementand semaforul. In mod uzual, un contor de semafor are de asemenea o limita superioara pentru a defini numarul de thread-uri ce pot executa wait() in mod concurent pe semaforul respectiv fara a se bloca. Prin setarea contorului de limita superioara la 1, va rezulta un semafor binar, ce poate fi utilizat in mod similar cu un lock pentru a sincroniza accesul la o sectiune critica.

In Java mecanismul de semaforizare este disponibil prin intermediul clasei java.util.concurrent.Semaphore. Constructorii clasei permit setarea contorului superior. Cele doua operatii de baza ale unui semafor au drept corespondent metodele acquire() pentru wait si respectiv release() pentru signal.


// exemplul pentru lock revizitat

import java.util.concurrent.Semaphore;

class SharedWork {

	Semaphore sem;			
	SomeObject sharedObject;

	SharedWork() {
		sem = new Semaphore(1);				//limita superioara contor; 1 va crea un semafor
								//binar cu aproximativ aceeasi functionalitate ca 
								//un mutex implementat folosind interfata Lock
		sharedObject = new SomeObject();
	}

	public void accessObject() {
		MyThread thread1 = new MyThread();
		MyThread thread2 = new MyThread();
		thread1.start();
		thread2.start();
	}

	class MyThread extends Thread {
		
		public void run() {
			while (true) {
				sem.acquire();			//thread-ul "obtine" semaforul prin decrementarea
								//contorului intern si interzice celoralte thread-uri
								//sa intre in sectiunea critica
				sharedObject.someMethod();	//executa o metoda care necesita acces exclusiv
				sem.release();			//thread-ul "elibereaza" semaforul
			}
		}
	}	
}

Una din principalele diferente intre un lock si un semafor este ca in cazul unui lock, odata ce acesta a fost blocat poate fi deblocat doar de thread-ul care l-a blocat. In cazul unui semafor, orice thread poate apela release.

Exercitii

1. Scrieti un program care porneste n si respectiv m thread-uri de doua tipuri diferite conform descrierii ce urmeaza. Aceste thread-uri acceseaza un contor partajat (initializat cu 0) intr-o bucla (100000 de iteratii). In fiecare iteratie thread-urile din primul tip citesc contorul intr-o variabila locala clasei thread-ului, o incrementeaza, si stocheaza valoarea rezultata inapoi in contorul partajat. Thread-urile din al doilea tip realizeaza acelasi tip de operatii dar in loc de incrementarea contorului il decrementeaza. Cand toate thread-urile finalizeaza operatiile, programul va afisa valoarea contorului si durata executiei (incercati o rulare pentru n = m). (2 puncte)

2. Pastrati prima varianta a programului. Modificati-l pentru a proteja accesul la contor in doua noi variante: folosind un ReentrantLock si folosind un semafor binar. Contorul trebuie sa fie 0 la finalul executiei pentru cazul n = m.
Rulati toate cele trei variante de implementare masurand timpul de rulare in milisecunde, folosind seturi egale m = n = 1, 2 si 4 thread-uri si centralizati rezultatele intr-un tabel comparativ. (1 punct)

Bonus points: Se acorda 1 punct bonus pentru prima prezentare completa a fiecarui exercitiu.
Termen rezolvare: end-of-lab-time minus 15 minute.

© 2022 Emanuel Onica. Parts of design by W3Layouts