[monitor] Incrocio

Si deve gestire la sincronizzazione di N_AUTO che attraversano un incrocio regolato da un semaforo. Il semaforo ha 4 stati: rosso da entrambi i lati, verde in “verticale”, rosso da entrambi i lati, verde in “orizzontale”. Le auto non devono passare con il rosso e scontrarsi. Si comportano secondo il seguente schema:

finché sulla strada:
    Se al semaforo:
        incrocio.semaforo(direzione); // direzione=true o false a seconda se orizzontale o verticale
 
    // muove la macchina c da (x,y) a (x+dx,y+dy)
    // la macchina si muove solo se la strada e' libera
    incrocio.muovi(c,x,y,dx,dy); 
    x+=dx; y+=dy;
 
incrocio.libera(x,y); // esce e libera (x,y)

Ecco un esempio di situazione con semaforo verde in verticale e rosso in orizzontale:

                      |  V     |                      
                      |        |
                      |        |                      
                      |        |
                      |        |                      
                      |        |
                      |  V     |                      
                      |        |
--------------------- o  V     o ---------------------

                         V        <  <  <        <    

 >        >     >  >     V  ^                         

--------------------- o     ^  o ---------------------
                      |        |
                      |  V     |                      
                      |        |
                      |        |                      
                      |        |                      
                      |        |
                      |        |                      
                      |        |

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 è occupata attende. Altrimenti scrive c in tale posizione e libera
    la posizione attuale x,y invocando libera(x,y)
  • void libera(int x, int y)
    libera la posizione x,y scrivendo uno spazio (viene invocata quando le auto escono dall’incrocio)
  • void semaforo(Boolean d)
    è il semaforo nell’incrocio. viene invocato con un booleano che indica la direzione
    (orizzontale o verticale). Il semaforo può essere verde in una direzione o rosso per entrambe
    se il semaforo e’ rosso per la direzione d la macchina attende
  • void cambiaSemaforo()
    viene invocata periodicamente e cambia lo stato del semaforo. Il semaforo deve ‘ciclare’ tra
    quattro stati: rosso per tutti, verde in una direzione, rosso per tutti, verde nell’altra direzione

Scaricare il file zip, modificare Incrocio.java e compilare e eseguire Automobile.java.

Visualizza la soluzione

/*
 * La verifica consiste nell'implementazione di un monitor per regolare il
 * movimento delle macchine nei pressi di un incrocio. In particolare, si deve:
 * - sincronizzare il movimento delle macchine in modo da evitare il
 *   verificarsi di tamponamenti;
 * - impedire l'attraversamento dell'incrocio se il semaforo è rosso.
 * Per implementare le operazioni richieste, il monitor usa le seguenti
 * variabili:
 * - una matrice di caratteri di dimensione pari all'incrocio, dove ogni cella
 *   contiene uno spazio se la corrispondente posizione è libera, altrimenti
 *   contiene un simbolo che rappresenta la macchina che sta occupando la
 *   corrispondente posizione dell'incrocio;
 * - un intero che codifica lo stato del semaforo: i valori 0 e 2 codificano il
 *   semaforo rosso in entrambe le direzioni, il valore 1 codifica il verde per
 *   le macchine che si muovono in verticale, il valore 3 codifica il verde per
 *   le macchine che si muovono in orizzonatale; questa scelta dei valori
 *   permette di implementare l'operazione di cambio di stato del semaforo con
 *   un semplice incremento modulo 4.
 */
public class Incrocio {
	private char[][] strada;
	private int semaforo;

	public Incrocio(int DIM) {
		/* Alloca una matrice di caratteri per rappresentare lo stato
		 * dell'incrocio e inizializzala con tutte le posizioni libere. */
		this.strada = new char[DIM][DIM];
		for (int i=0; i<DIM; i++) {
			for (int j=0; j<DIM; j++) {
				strada[i][j] = ' ';
			}
		}
		/* Inizialmente il semaforo è rosso in tutte le direzioni
		 * (successivamente diventa verde quello in direzione verticale,
		 * quindi usiamo il valore 0 e non il 2). */
		this.semaforo = 0;
	}

	public synchronized void muovi(char c, int x, int y, int dx, int dy) throws InterruptedException {
		/* Attendi se la posizione in cui si deve spostare la macchina è
		 * occupata, in modo da evitare tamponamenti. */
		while (strada[x+dx][y+dy] != ' ') {
			wait();
		}
		/* Occupa la posizione e libera quella precedentemente occupata. */
		strada[x+dx][y+dy]= c;
		libera(x,y);
	}

	public synchronized void libera(int x, int y) {
		/* Ora la posizione è libera. */
		strada[x][y] = ' ';
		/* Notifica il cambiamento alle altre macchine: se una di queste stava
		 * attendendo la liberazione della posizione (x, y), ora può riprovare
		 * ad occuparla. */
		notifyAll();

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

	public synchronized void semaforo(Boolean d) throws InterruptedException {
		/* Attendi se il semaforo relativo alla mia direzione di marcia è rosso.
		 * La variabile 'd' è 'true' se la direzione di spostamento è verticale,
		 * 'false' se orizzontale. */
		while ((d && semaforo != 1) || (!d && semaforo !=3)) {
			wait();
		}
	}

	public synchronized void cambiaSemaforo() {
		/* Cambia lo stato del semaforo, come descritto sopra. */
		semaforo = (semaforo + 1) % 4;
		/* Notifica il cambiamento alle altre macchine: se una macchina era
		 * in attesa del verde, ora può avere la possibilità di muoversi.
		 * Come piccola ottimizzazione, evitiamo la notifica nel caso il
		 * semaforo diventi rosso in tutte le direzioni, in quanto nessuna
		 * macchina ferma al semaforo potrà muoversi. */
		if (semaforo % 2 != 0) {
			notifyAll();	
		}
	}
}