MySQL Java JavaScript PHP Python HTML-CSS C-sharp

Condiții în blocări

Utilizarea condițiilor în blocări permite controlul asupra gestionării accesului la fire de execuție. O condiție de blocare reprezintă un obiect al interfeței Condition din pachetul java.util.concurrent.locks.

Utilizarea obiectelor Condition este similară cu folosirea metodelor wait/notify/notifyAll din clasa Object, discutate anterior. În particular, putem folosi următoarele metode ale interfeței Condition:

  • await: firul de execuție așteaptă până când se îndeplinește o anumită condiție și până când alt fir de execuție apelează metodele signal/signalAll. Este similară cu metoda wait din clasa Object
  • signal: semnalizează că firul de execuție care a apelat anterior metoda await() poate continua lucrul. Utilizarea este similară cu metoda notify din clasa Object
  • signalAll: semnalizează tuturor firelor care au apelat metoda await() că pot continua lucrul. Este similară cu metoda notifyAll() din clasa Object

Aceste metode sunt apelate în blocul de cod care intră sub acțiunea blocării ReentrantLock. Mai întâi, folosind această blocare, trebuie să obținem obiectul Condition:

ReentrantLock locker = new ReentrantLock();
Condition condition = locker.newCondition();

De obicei, se verifică întâi condiția de acces. Dacă se respectă condiția, firul de execuție așteaptă până când aceasta se schimbă:

while (condiție)
   condition.await();

După finalizarea tuturor acțiunilor, celorlalte fire li se semnalizează schimbarea condiției:

condition.signalAll();

Este important să apelăm metoda signal/signalAll la final, pentru a evita posibilitatea blocării reciproce a firelor de execuție.

Pentru exemplu, vom modifica sarcina din tema despre metodele wait/notify, utilizând obiectul Condition.

Avem un depozit în care pot fi plasate simultan maximum 3 produse. Producătorul trebuie să producă 5 produse, iar cumpărătorul trebuie să le achiziționeze. Totodată, cumpărătorul nu poate cumpăra un produs dacă nu există produse în depozit:

import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;

public class Program {
 
   public static void main(String[] args) {
         
       Store store = new Store();
       Producer producer = new Producer(store);
       Consumer consumer = new Consumer(store);
       new Thread(producer).start();
       new Thread(consumer).start();
   }
}

// Clasa Magazin, care stochează produsele produse
class Store {
  private int product = 0;
  ReentrantLock locker;
  Condition condition;
   
  Store() {
      locker = new ReentrantLock(); // creăm blocarea
      condition = locker.newCondition(); // obținem condiția asociată cu blocarea
  }
   
  public void get() {
     locker.lock();
     try {
         // cât timp nu sunt produse disponibile, așteptăm
         while (product < 1)
             condition.await();
         
         product--;
         System.out.println("Cumpărătorul a cumpărat 1 produs");
         System.out.println("Produse în depozit: " + product);
         
         // semnalizăm
         condition.signalAll();
     }
     catch (InterruptedException e) {
         System.out.println(e.getMessage());
     }
     finally {
         locker.unlock();
     }
  }

  public void put() {
      locker.lock();
      try {
         // cât timp sunt 3 produse în depozit, așteptăm eliberarea spațiului
         while (product >= 3)
             condition.await();
         
         product++;
         System.out.println("Producătorul a adăugat 1 produs");
         System.out.println("Produse în depozit: " + product);
         // semnalizăm
         condition.signalAll();
     }
     catch (InterruptedException e) {
         System.out.println(e.getMessage());
     }
     finally {
         locker.unlock();
     }
  }
}

// Clasa Producător
class Producer implements Runnable {
 
   Store store;
   Producer(Store store) {
      this.store = store;
   }
   public void run() {
       for (int i = 1; i < 6; i++) {
           store.put();
       }
   }
}

// Clasa Consumator
class Consumer implements Runnable {
     
   Store store;
   Consumer(Store store) {
      this.store = store;
   }
   public void run() {
       for (int i = 1; i < 6; i++) {
           store.get();
       }
   }
}

În final, vom obține o ieșire asemănătoare cu aceasta:

Producătorul a adăugat 1 produs
Produse în depozit: 1
Producătorul a adăugat 1 produs
Produse în depozit: 2
Producătorul a adăugat 1 produs
Produse în depozit: 3
Cumpărătorul a cumpărat 1 produs
Produse în depozit: 2
Cumpărătorul a cumpărat 1 produs
Produse în depozit: 1
Cumpărătorul a cumpărat 1 produs
Produse în depozit: 0
Producătorul a adăugat 1 produs
Produse în depozit: 1
Producătorul a adăugat 1 produs
Produse în depozit: 2
Cumpărătorul a cumpărat 1 produs
Produse în depozit: 1
Cumpărătorul a cumpărat 1 produs
Produse în depozit: 0
← Lecția anterioară Lecția următoare →