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();
}
}