Oggi si nuota! La verifica consiste nell’implementare un monitor per la sincronizzare dei thread che rappresentano i nuotatori di diverse squadre e lo starter della gara. La competizione si svolge su una piscina suddivisa in corsie, ciascuna delle quali è occupata ad una squadra. Lo starter dà il via quando il primo nuotatore di ogni squadra è schierato sulla griglia di partenza. Ogni nuotatore deve fare due vasche prima di lasciare il posto al nuotatore successivo della propria squadra.
Dovete implementare i seguenti metodi della classe Piscina.java contenuta nel file ZIP:
- void chiediMutex(int x, int y): chiede il mutex per accedere alla posizione (x, y). Viene invocato dai nuotatori prima di eseguire lo spostamento.
- void rilasciaMutex(int x, int y): rilascia il mutex in posizione (x, y). Viene invocato dai nuotatori quando abbandonano la posizione (x, y).
- void attendiPartenza(int corsia): il nuotatore è schierato in bordo vasca e deve attendere il momento in cui può partire.
- void arrivato(int corsia): il nuotatore ha terminato le due vasche, il successivo può partire.
- void attendiSchieramento(): viene invocato dallo starter per attendere che tutti i (primi) nuotatori siano schierati.
- void daiVia(): a schieramento completato, lo starter dà inizio alla gara.
Un nuotatore esegue le seguenti operazioni:
- raggiunge il bordo vasca (usando chiediMutex e rilasciaMutex);
- attende di potersi tuffare (invocando attendiPartenza);
- si tuffa e fa le 2 vasche (usando chiediMutex e rilasciaMutex);
- notifica che è arrivato (tramite la funzione arrivato);
- esce dalla vasca e si posiziona all’esterno (usando chiediMutex e rilasciaMutex).
Lo starter attende che i nuotatori si siano schierati (usando attendiSchieramento) e dà il via alla gara (usando daiVia).
/*
* La verifica consiste nell'implementazione di un monitor che permetta lo svolgersi di una gara di nuoto in cui:
* - due nuotatori non possano occupare contemporaneamente la medesima posizione dello schema (costituito dalla
* piscina e la zona esterna sulla sinistra);
* - lo starter deve consentire l'inizio della gara solo quando tutti i nuotatori siano schierati;
* - i nuotatori della batteria iniziale devono attendere l'inizio della gara, dichiarato dallo starter;
* - ogni nuotatore deve attendere che il precedente abbia terminato le due vasche prima di tuffarsi a sua volta.
*
* Per realizzare la sincronizzazione desiderata, sono usate le seguenti strutture dati:
* - 'schema': matrice in cui ogni posizione indica se la corrispondente cella dello schema è occupata da un
* nuotatore ('true' rappresenta una cella occupata, 'false' una cella libera);
* - 'schierato': vettore che indica se un nuotatore è schierato nella corrispondente corsia (usato per la
* fase iniziale);
* - 'partenza': vettore che indica se il corrispondente nuotatore può tuffarsi. Il vettore è usato sia
* all'avvio della gara (quando sarà modificato dallo starter), sia per gestire il succedersi dei nuotatori
* (quando verrà modificato dal nuotatore precedente nella corsia).
*/
public class Piscina {
private int x, y, corsie;
private boolean[] schierato;
private boolean[] partenza;
private boolean[][] schema;
public Piscina(int x, int y, int corsie) {
this.x = x;
this.y = y;
this.corsie = corsie;
/* NB. Da specifica del linguaggio Java, tutte le variabili booleane sono inizializzate a 'false'
* di default. */
schema = new boolean[x][y];
schierato = new boolean[corsie];
partenza = new boolean[corsie];
}
public synchronized void chiediMutex(int x, int y) throws InterruptedException {
/* Attendi finché la cella non si libera. */
while (schema[x][y]) {
wait();
}
/* Segnala la cella come occupata. */
schema[x][y] = true;
}
public synchronized void rilasciaMutex(int x, int y) {
/* Libera la cella e notifica eventuali nuotatori in attesa. */
schema[x][y] = false;
notifyAll();
}
public synchronized void attendiPartenza(int corsia) throws InterruptedException {
/* Il nuotatore è schierato: notifica lo starter. Questa parte viene eseguita da parte di ogni nuotatore,
* ma non è rilevante per quelli che non fanno parte del gruppo iniziale. */
schierato[corsia] = true;
notifyAll();
/* Attendi il permesso di partire da parte dello starter o dal nuotatore precedente. */
while (!partenza[corsia]) {
wait();
}
/* Impedisci la partenza al nuotatore successivo. */
partenza[corsia] = false;
}
public synchronized void arrivato(int corsia) {
/* Il nuotatore ha terminato le vasche: notifica la possibilità di partire al successivo. */
partenza[corsia] = true;
notifyAll();
}
public synchronized void attendiSchieramento() throws InterruptedException {
/* Attendi fino a che tutti i nuotatori non sono schierati. */
for (int i = 0; i < corsie; i++) {
while (!schierato[i]) {
wait();
}
}
}
public synchronized void daiVia() {
/* Notifica a tutti i nuotatori schierati di partire. */
for (int i = 0; i < corsie; i++) {
partenza[i] = true;
}
notifyAll();
}
}
Si poteva risolvere il via dello starter con un contatore “partenza” inizializzato a zero, in questo modo:
public synchronized void attendiPartenza(int corsia) throws InterruptedException {
partenza++;
notifyAll();
while(!attesa[corsia]){
wait();
}
attesa[corsia]=false;
}
public synchronized void attendiSchieramento() throws InterruptedException {
while(partenza<corsie){
wait();
}
}
public synchronized void daiVia() {
for(int i=0;i<corsie;i++){
partenza[i]=true;
}
notifyAll();
}
}