Il problema del produttore-consumatore è caratterizzato da due task: il produttore e il consumatore, che comunicano attraverso un buffer, il quale ha una capacità limitata:
Gli aspetti da gestire in questa situazione sono:
Quando il buffer è pieno, il produttore non ha spazio per inserire i dati, quindi deve sospendere la sua esecuzione. Successivamente, quando il consumatore preleva un elemento dal buffer pieno, liberando uno spazio, esso “sveglia” il produttore, che potrà allora ricominciare a depositare elementi nel buffer.
Analogamente, il consumatore si deve sospendere quando il buffer è vuoto, poichè non ci sono dati da prelevare, e viene poi risvegliato da un produttore, dopo che quest’ultimo ha depositato un elemento nel buffer vuoto.
Questa soluzione può non essere implementata tramite le primitive di comunicazione tra thread: synchronized
, wait()
, notify()
, e notifyAll()
. Bisogna però fare attenzione a non introdurre race condition, deadlock, o anche semplicemente errori di logica appliativa.
Per ottenere un’implementazione corretta, si definisce la classe CellaCondivisa
in modo che:
A scopo illustrativo, viene aggiunto anche un metodo che restituisce il numero di elementi attualmente nel buffer; essendo di sola lettura, questo non ha bisogno di essere synchronized
.
public class CellaCondivisa {
private static final int BUFFER_SIZE = 1;
private int numItems = 0;
private int value;
public synchronized int getItem() throws InterruptedException {
if (numItems == 0) {
// Se non c'è niente da leggere,
// chi cerca di farlo viene fermato.
wait();
}
numItems--;
// Siccome il buffer non è più pieno,
// viene svegliato un eventuale produttore in attesa.
notify();
return value;
}
public synchronized void setItem(int v) throws InterruptedException {
if (numItems == BUFFER_SIZE) {
// Se il buffer è pieno,
// chi cerca di scrivere va in attesa.
wait();
}
value = v;
numItems++;
// Adesso il buffer non è più vuoto,
// quindi si sveglia un eventuale consumatore in attesa.
notify();
}
public int getCurrentSize() {
return numItems;
}
}