Preparazione verifica: Java, thread e semafori (Pizzeria)
Teoria
- Chi fa l'azione (Lavoratori):
Pizzaiolo e Fattorino implementano Runnable e agiscono in autonomia dentro i Thread.
- Chi viene usato (Risorse): La
Pizzeria è l'oggetto passivo che subisce le azioni dei lavoratori e non usa Runnable.
Comandi studiati
- Classi:
public class Nome { ... }, campi private, costruttore con this.campo = campo;, campi e metodi static
- Thread:
implements Runnable, @Override public void run() { ... }, new Thread(...), t.start(), t.join(), array di thread Thread[] v = new Thread[4];
- Pausa:
Thread.sleep(ms); dentro try { ... } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
- Numero casuale:
Random rand = new Random(); con rand.nextInt(100, 301) (da 100 a 300), oppure Math.random()
- Sincronizzazione:
synchronized, wait(), notifyAll(), solo dentro la classe Semaforo, che è una classe scritta da noi (non si usano le classi di java.util.concurrent)
- Semafori:
new Semaforo(n) con i metodi P() e V()
- Array:
int[] a = new int[3];, a.length, scorrere con for
- Stampare:
System.out.println(...) unendo i testi con +
- Cicli e condizioni:
for, while, if
- Contatori:
count++ e count--
- Metodi che restituiscono un valore (
return) e metodi get...()
CLASSE SEMAFORO
public class Semaforo {
private int count;
public Semaforo(int count) {
this.count = count;
}
public synchronized void P(){
while (count == 0){
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
count--;
}
public synchronized void V(){
count++;
notifyAll();
}
}
CLASSE PIZZERIA (RISORSA CONDIVISA)
public class Pizzeria {
private Semaforo sPosti;
private Semaforo sPizze;
private Semaforo sBanconeVuoto;
private Semaforo sBanconePieno;
private Semaforo mutexBancone;
private int bancone;
private int[] arrayBancone;
private static int nPizze = 0;
// Costruttore della pizzeria che riceve i semafori di sincronizzazione
public Pizzeria(Semaforo sPosti, Semaforo sPizze, Semaforo sBanconeVuoto, Semaforo sBanconePieno) {
this.sPosti = sPosti;
this.sPizze = sPizze;
this.sBanconeVuoto = sBanconeVuoto;
this.sBanconePieno = sBanconePieno;
this.mutexBancone = new Semaforo(1); // Mutex per proteggere l'accesso esclusivo al bancone
this.bancone = 0;
this.arrayBancone = new int[3]; // Buffer limitato con capacità massima di 3 pizze
}
// Metodo eseguito dai pizzaioli per cuocere e depositare la pizza
public void inforna(String nome){
// Richiede un posto libero nel forno (max 2 pizze contemporaneamente)
sPosti.P();
System.out.println(nome + " ha infornato una pizza");
try {
// Simula il tempo di cottura della pizza (400ms)
Thread.sleep(400);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(nome + " ha sfornato una pizza");
// Rilascia il posto nel forno
sPosti.V();
// Sezione critica: assegna un ID progressivo unico alla pizza in modo sicuro
sPizze.P();
nPizze++;
int idPizza = nPizze;
sPizze.V();
// Attende che ci sia almeno uno spazio vuoto sul bancone
sBanconeVuoto.P();
// Sezione critica: deposita la pizza sul bancone condiviso
mutexBancone.P();
arrayBancone[bancone] = idPizza;
bancone++;
mutexBancone.V();
// Segnala che è presente una nuova pizza pronta sul bancone
sBanconePieno.V();
}
// Metodo eseguito dai fattorini per prelevare una pizza dal bancone
public int prendiPizza(String nome){
// Attende che ci sia almeno una pizza pronta sul bancone
sBanconePieno.P();
// Sezione critica: preleva la prima pizza disponibile
mutexBancone.P();
int idPizza = arrayBancone[0];
// Sposta tutti gli elementi rimanenti a sinistra (shift dell'array)
for (int i = 0; i < bancone - 1; i++) {
arrayBancone[i] = arrayBancone[i + 1];
}
bancone--;
System.out.println(nome + " ha preso la pizza " + idPizza + " dal bancone");
mutexBancone.V();
// Segnala che si è liberato uno spazio sul bancone
sBanconeVuoto.V();
return idPizza;
}
// Restituisce il numero totale di pizze preparate nella simulazione
public static int getnPizze() {
return nPizze;
}
}
PIZZAIOLO (THREAD)
import java.util.Random;
public class Pizzaiolo implements Runnable{
private String nome;
private Pizzeria forno;
private int nPizze;
// Costruttore per inizializzare il pizzaiolo
public Pizzaiolo(String nome, Pizzeria forno, int nPizze) {
this.nome = nome;
this.forno = forno;
this.nPizze = nPizze;
}
@Override
public void run() {
Random rand = new Random();
// Ciclo in cui il pizzaiolo prepara il numero assegnato di pizze
for (int i = 1; i <= nPizze; i++){
try {
// Simula il tempo di impasto/preparazione con un ritardo casuale (tra 100ms e 300ms)
Thread.sleep(rand.nextInt(100, 301));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(nome + " ha preparato una pizza");
// Inforna la pizza nella risorsa condivisa (Pizzeria)
forno.inforna(nome);
}
}
}
FATTORINO (THREAD)
public class Fattorino implements Runnable{
private String nome;
private Semaforo mioTurno;
private Semaforo prossimoTurno;
private int consegneEffettuate;
private Pizzeria bancone;
// Costruttore per inizializzare il fattorino, i semafori di turno e il riferimento alla pizzeria
public Fattorino(String nome, Semaforo mioTurno, Semaforo prossimoTurno, Pizzeria bancone) {
this.nome = nome;
this.mioTurno = mioTurno;
this.prossimoTurno = prossimoTurno;
this.bancone = bancone;
this.consegneEffettuate = 0;
}
@Override
public void run() {
// Ogni fattorino esegue un ciclo di 8 consegne (per un totale di 24 consegne coordinate)
for (int i = 0; i < 8; i++){
// Attende il proprio turno (gestisce la turnazione circolare/round-robin tra i fattorini)
mioTurno.P();
// Preleva una pizza dal bancone condiviso (operazione bloccante se il bancone è vuoto)
bancone.prendiPizza(nome);
try {
// Simula il tempo necessario per effettuare la consegna (200ms)
Thread.sleep(200);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// Incrementa il contatore delle consegne completate con successo da questo fattorino
consegneEffettuate++;
System.out.println(nome + " ha completato una consegna");
// Segnala la fine del proprio turno, sbloccando il fattorino successivo nella catena
prossimoTurno.V();
}
}
// Restituisce il numero totale di consegne effettuate dal fattorino
public int getConsegneEffettuate() {
return consegneEffettuate;
}
// Restituisce il nome identificativo del fattorino
public String getNome() {
return nome;
}
}
MAIN
public class Main {
public static void main(String[] args) {
// Inizializzazione dei semafori per la gestione della pizzeria
Semaforo sPosti = new Semaforo(2); // Regola la capienza massima del forno (massimo 2 pizze infornate)
Semaforo sPizze = new Semaforo(1); // Mutex per l'incremento sicuro dell'ID progressivo delle pizze
Semaforo sBanconeVuoto = new Semaforo(3); // Capienza massima del bancone (massimo 3 pizze)
Semaforo sBanconePieno = new Semaforo(0); // Conta le pizze attualmente presenti e pronte sul bancone
// Creazione dell'istanza condivisa della pizzeria
Pizzeria pizzeria = new Pizzeria(sPosti, sPizze, sBanconeVuoto, sBanconePieno);
// Semafori per la turnazione sequenziale (round-robin) dei fattorini
Semaforo sF1 = new Semaforo(1);
Semaforo sF2 = new Semaforo(0);
Semaforo sF3 = new Semaforo(0);
// Creazione dei tre fattorini con i rispettivi semafori di turno
Fattorino f1 = new Fattorino("F1", sF1, sF2, pizzeria);
Fattorino f2 = new Fattorino("F2", sF2, sF3, pizzeria);
Fattorino f3 = new Fattorino("F3", sF3, sF1, pizzeria);
// Creazione dei thread per i fattorini
Thread T1 = new Thread(f1);
Thread T2 = new Thread(f2);
Thread T3 = new Thread(f3);
// Avvio dei thread dei fattorini
T3.start();
T2.start();
T1.start();
// Creazione e avvio di 4 pizzaioli, ciascuno dei quali prepara 6 pizze
Thread[] pizzaioli = new Thread[4];
for (int i = 0; i < pizzaioli.length; i++){
pizzaioli[i] = new Thread(new Pizzaiolo(("Pizzaiolo " + (i+1)), pizzeria, 6));
pizzaioli[i].start();
}
// Attesa del completamento di tutti i thread dei pizzaioli
for (int i = 0; i < pizzaioli.length; i++){
try {
pizzaioli[i].join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// Attesa del completamento dei thread dei fattorini
try {
T1.join();
T2.join();
T3.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// Stampa del riepilogo finale della simulazione
System.out.println("Riepilogo");
System.out.println("Sono state preparate: " + Pizzeria.getnPizze() + " pizze");
System.out.println(f1.getNome() + " ha consegnato: " + f1.getConsegneEffettuate() + " pizze");
System.out.println(f2.getNome() + " ha consegnato: " + f2.getConsegneEffettuate() + " pizze");
System.out.println(f3.getNome() + " ha consegnato: " + f3.getConsegneEffettuate() + " pizze");
}
}