Sari la conținutul principal

Semafoare

Un semafor poate fi văzut ca un lock ce permite mai multor thread-uri să coexiste într-o anumită regiunie critică la un moment dat. Semaforul folosește un contor care determină câte thread-uri mai pot intra. Odată ajuns la semafor, un thread este lăsat să intre doar dacă numărul de thread-uri aflate în zona critică este mai mic decât numărul maxim de thread-uri setat la crearea semaforului.

Pe un semafor se pot realiza următoarele operații:

  • acquire - se încearcă trecerea de semafor; dacă numărul de thread-uri aflate în zona critică este mai mic decât numărul maxim de thread-uri acceptate, thread-ul poate intra
  • release - un thread apeleză metoda pentru a anunța faptul că și-a terminat treaba în zona critică, permițând astfel incrementarea numărului de thread-uri car pot intra în zona critică
class Something {

static int resource = 0;
}

public class MySemaphore extends Thread {

private int id;
private Semaphore sem;

public MySemaphore(int id, Semaphore sem) {
this.id = id;
this.sem = sem;
}

public static void main(String args[]) {
Semaphore sem = new Semaphore(1);
MySemaphore mt1 = new MySemaphore(0, sem);
MySemaphore mt2 = new MySemaphore(1, sem);

mt1.start();
mt2.start();

try {
mt1.join();
mt2.join();
} catch (InterruptedException ex) {
Logger.getLogger(MySemaphore.class.getName()).log(Level.SEVERE, null, ex);
}

System.out.println("count: " + Something.resource);
}

@Override
public void run() {
switch (this.id) {
case 0:
System.out.println("Starting thread " + id);
try {
System.out.println("Thread " + id + " is waiting for a permit.");
sem.acquire();
System.out.println("Thread " + id + " gets a permit.");

for (int i = 0; i < 5; i++) {
Something.resource++;
System.out.println("Thread " + id + ": " + Something.resource);
Thread.sleep(10);
}
} catch (InterruptedException exc) {
System.out.println(exc);
}

System.out.println("Thread " + id + " releases the permit.");
sem.release();
break;
case 1:
System.out.println("Starting thread " + id);
try {
System.out.println("Thread " + id + " is waiting for a permit.");
sem.acquire();
System.out.println("Thread " + id + " gets a permit.");

for (int i = 0; i < 5; i++) {
Something.resource--;
System.out.println("Thread " + id + ": " + Something.resource);
Thread.sleep(10);
}
} catch (InterruptedException exc) {
System.out.println(exc);
}

// Release the permit.
System.out.println("Thread " + id + " releases the permit.");
sem.release();
break;
}
}

public int getThreadId() {
return this.id;
}
}

În codul de mai sus se creează un semafor ce acceptă un singur thread în zona critică (observați forma constructorului Semaphore). Practic, acest semafor este similar cu un lock. Semaforul se creează in main si se trimite ca parametru în constructorul thread-urilor. În cadrul metodei run, thread-urile încearcă să apeleze acquire, însă doar unul dintre ele va reuși să intre în zona de după semafor. Ulterior, după ce se realizează operațiile din zona critică, thread-ul respectiv anunță faptul că a ieșit prin intermediul unui apel de release. Observați faptul că, în funcție de thread-ul care reușește să intre în zona respectivă, contorul poate scădea până la -5, urmând să revină la 0 apoi, sau să fie crescut până la 5, urmând să ajungă la 0 apoi.

CheatSheet Semaphore