[monitor] Incrocio con pedoni

Ci sono automobili e pedoni che si approcciano a un incrocio. I pedoni hanno la precedenza sulle auto.

Le auto si comportano secondo il seguente schema:

finche' sulla strada:

	Se accede all'incrocio:
		incr.arrivaAuto(); // indica che sta per attraversare
	Se esce dall'incrocio:
		incr.attraversatoAuto(); // indica che ha attraversato
				
	incr.muovi(c,x,y,dx,dy); // la macchina si muove se ha strada
	x+=dx; y+=dy;
		
incr.libera(x,y); // esce dalla strada

I pedoni si comportano secondo il seguente schema:

finche' sulla strada:

	Se accede all'incrocio:
		incr.arrivaPedone(); // indica che sta per attraversare
	Se esce dall'incrocio:
		incr.attraversatoPedone(); // indica che ha attraversato
				
	incr.muovi(c,x,y,dx,dy); // il pedone si muove se ha strada
	x+=dx; y+=dy;
		
incr.libera(x,y); // esce dalla strada

Progettare un monitor Incrocio che implementi i seguenti metodi:

 void muovi(char c, int x, int y, int dx, int dy)
   se la posizione x+dx,y+dy e' occupata attende. Altrimenti scrive c in tale posizione e libera
   la posizione attuale x,y scrivendo uno spazio

 void libera(int x, int y)
   libera la posizione x,y scrivendo uno spazio
 
 char strada(int x, int y)
   ritorna il carattere (auto o spazio) in posizione x,y (serve per stampare le auto)

 void arrivaAuto() 
   Se ci sono pedoni in attesa o in attraversamento attende

 void attraversatoAuto() 
   Indica che ha finito di attraversare l'incrocio

 void arrivaPedone() 
   Se ci sono auto in attraversamento attende, ma ha la precedenza

 void attraversatoPedone() 
   Indica che ha finito di attraversare l'incrocio

In particolare notare che le auto attendono se ci sono pedoni in attesa o attraversamento (dando quindi la precedenza ai pedoni). I pedoni, invece, attendono solo se ci sono auto in attraversamento.

Scaricare qui il codice sorgente.

Visualizza la soluzione

Soluzione

Segue una possibile soluzione commentata della verifica:

/*
 * Descrizione generale: utilizziamo 2 contatori
 * 
 * - pedoni: numero di pedoni in attesa o attraversamento
 * - auto: numero di auto in attraversamento
 * 
 * Il contatore pedoni viene incrementato all'inizio di arrivaPedone() in modo da
 * conteggiare sia i pedoni in attesa che quelli in attraversamento. Il contatore
 * auto invece viene incrementato dopo che l'auto ha atteso eventuali pedoni,
 * alla fine del metodo arrivaAuto(), in modo da conteggiare solo le auto in
 * attraversamento (e non quelle in attesa). In questo modo è sufficiente che le
 * auto attendano finché pedoni>0 e che i pedoni attendano finché auto>0.
 * L'ultimo thread auto (o pedone) che esce dall'incrocio esegue una notifyAll
 * per risvegliare eventuali pedoni (o auto) in attesa di poter attraversare
 * l'incrocio.
 * 
 * Dentro il metodo muovi bisogna attendere finché strada[x+dx][y+dy] != ' ', per
 * evitare che auto e pedoni si sovrappongano (l'accesso alla strada deve
 * avvenire in mutua esclusione). Quando viene liberata una cella (metodo libera)
 * si deve fare una notifyAll per sbloccare eventuali pedoni o auto in attesa
 * dentro il metodo muovi.
 */

public class Incrocio {
	int DIM;
	char[][] strada;
	int pedoni = 0; // numero di pedoni in attesa o attraversamento
	int auto = 0;	// numero di auto in attraversamento

	public Incrocio(int DIM) {
		int i,j;
		this.DIM = DIM;
		this.strada = new char[DIM][DIM];
		for (i=0;i<DIM;i++)
			for (j=0;j<DIM;j++)
				strada[i][j] = ' ';
	}

	public synchronized void muovi(char c, int x, int y, int dx, int dy) throws InterruptedException{
		// attende finché la cella in cui deve muoversi è occupata
		while (strada[x+dx][y+dy] != ' ')
			wait();
		strada[x+dx][y+dy]= c;
		libera(x,y);
	}

	public synchronized void libera(int x, int y) {
		strada[x][y] = ' ';
		// notifica eventuali auto o pedoni in attesa di muoversi (metodo muovi)
		notifyAll(); 

	}
	public char strada(int x, int y) {
		return strada[x][y];
	}


	public synchronized void arrivaAuto() throws InterruptedException {
		// finché ci sono pedoni in attesa o attraversamento attende
		// (precedenza ai pedoni)
		while(pedoni>0)
			wait();
		auto += 1; // un'auto in più in attraversamento

	}

	public synchronized void attraversatoAuto() {
		auto -= 1; // un'auto in meno in attraversamento
		if (auto == 0)
			// se è l'ultima auto nell'incrocio notifica eventuali pedoni in attesa
			notifyAll(); 
	}
	public synchronized void arrivaPedone() throws InterruptedException {
		pedoni += 1; // un pedone in più in attesa o attraversamento
		// finché ci sono auto in attraversamento attende (non attende auto in
		// attesa, perché i pedoni hanno la precedenza)
		while(auto > 0)
			wait(); 
	}

	public synchronized void attraversatoPedone() {
		pedoni -= 1; // un pedone in meno in attraversamento o attesa
		if (pedoni == 0)
			// se è l'ultimo pedone nell'incrocio notifica eventuali auto in attesa
			notifyAll(); 
	}
}