MySQL Java JavaScript PHP Python HTML-CSS C-sharp C++ Go

Metodele wait și notify

Uneori, în interacțiunea între thread-uri, apare necesitatea de a notifica un thread despre acțiunile altui thread. De exemplu, acțiunile unui thread pot depinde de rezultatele acțiunilor altui thread, și este nevoie să notificăm un thread că altul a finalizat o anumită sarcină. Pentru astfel de situații, clasa Object definește o serie de metode:

  • wait(): eliberează monitorul și pune thread-ul apelant în starea de așteptare până când un alt thread va apela metoda notify()
  • notify(): reia execuția thread-ului care a apelat anterior metoda wait()
  • notifyAll(): reia execuția tuturor thread-urilor care au apelat anterior metoda wait()

Toate aceste metode trebuie apelate doar dintr-un context sincronizat – adică dintr-un bloc sau o metodă sincronizată.

Să vedem cum putem folosi aceste metode. Vom lua un exemplu clasic din tema precedentă – "Producător-Consumator": în acest caz, consumatorul nu poate cumpăra un produs până când producătorul nu îl produce.

Să presupunem că producătorul trebuie să producă 5 produse, iar consumatorul trebuie să le achiziționeze pe toate. Totuși, la un moment dat, în depozit nu pot fi mai mult de 3 produse. Pentru a rezolva această problemă, folosim metodele wait() și notify():

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 fabricate
class Store {
  private int product = 0;
 
  public synchronized void get() {
     while (product < 1) {
        try {
           wait();
        } catch (InterruptedException e) {}
     }
     product--;
     System.out.println("Consumatorul a cumpărat 1 produs");
     System.out.println("Produse pe stoc: " + product);
     notify();
  }
 
  public synchronized void put() {
     while (product >= 3) {
        try {
           wait();
        } catch (InterruptedException e) {}
     }
     product++;
     System.out.println("Producătorul a adăugat 1 produs");
     System.out.println("Produse pe stoc: " + product);
     notify();
  }
}

// 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();
       }
   }
}

Aici sunt definite clasele Store, Producer și Consumer. Producătorul, în metoda run(), adaugă 5 produse în obiectul Store folosind metoda sa put(). Consumatorul, în metoda run(), apelează în ciclu metoda get() a obiectului Store pentru a obține produsele. Ambele metode din Store – put() și get() – sunt sincronizate.

Pentru a urmări disponibilitatea produselor în clasa Store, verificăm valoarea variabilei product. Implicit, nu există niciun produs, astfel că variabila este egală cu 0. Metoda get() (de obținere a produsului) trebuie să funcționeze doar când există cel puțin un produs. De aceea, în metoda get() verificăm dacă produsul este absent:

while (product < 1)

Dacă nu există produs, se apelează metoda wait(). Aceasta eliberează monitorul obiectului Store și blochează execuția metodei get() până când, pentru același monitor, se va apela metoda notify().

Când în metoda put() se adaugă un produs și se apelează notify(), metoda get() obține monitorul și iese din construcția while (product < 1), deoarece produsul a fost adăugat. Apoi se simulează achiziția produsului de către consumator – se afișează un mesaj, iar valoarea variabilei product este scăzută: product--. La final, apelul metodei notify() semnalează metodei put() că poate continua execuția.

În metoda put() funcționează o logică similară, dar de această dată metoda put() trebuie să funcționeze doar dacă există mai puțin de trei produse în magazin. Prin urmare, se verifică existența produselor în ciclu, și dacă produsele există deja, monitorul este eliberat cu ajutorul metodei wait() și așteptăm apelul metodei notify() din get().

Rezultatul programului va fi următorul:

Producătorul a adăugat 1 produs
Produse pe stoc: 1
Producătorul a adăugat 1 produs
Produse pe stoc: 2
Producătorul a adăugat 1 produs
Produse pe stoc: 3
Consumatorul a cumpărat 1 produs
Produse pe stoc: 2
Consumatorul a cumpărat 1 produs
Produse pe stoc: 1
Consumatorul a cumpărat 1 produs
Produse pe stoc: 0
Producătorul a adăugat 1 produs
Produse pe stoc: 1
Producătorul a adăugat 1 produs
Produse pe stoc: 2
Consumatorul a cumpărat 1 produs
Produse pe stoc: 1
Consumatorul a cumpărat 1 produs
Produse pe stoc: 0

Astfel, folosind metoda wait() în metoda get(), așteptăm ca producătorul să adauge un nou produs. După adăugare, se apelează notify(), semnalând că există un loc liber în magazin și se pot adăuga mai multe produse.

În metoda put(), folosind wait(), așteptăm eliberarea unui loc în magazin. După ce locul devine disponibil, adăugăm un produs și prin notify() notificăm consumatorul că poate lua produsul.