Un Un esercizio esercizio d d ’ ’ esame esame e note
e note sulla sulla gestione gestione dei dei dati dati
Flavio De Paoli
Il testo
Il testo
(Appello 22 ottobre 2001)(Appello 22 ottobre 2001) Una stazione di servizio dispone di n distributori di benzina. I clienti si presentano a uno dei distributori e chiedono una certa quantità di benzina. Se il distributore è in grado di soddisfare la richiesta, il cliente viene servito;
altrimenti, rinuncia. A intervalli arbitrari, un’autocisterna rifornisce i distributori; il rifornimento può avvenire solo se i distributori sono liberi, e durante il rifornimento i clienti non possono accedere a nessun distributore. I distributori sono inizialmente pieni, e ogni rifornimento li riempie di nuovo.
Il testo
Il testo
(Appello 22 ottobre 2001)(Appello 22 ottobre 2001) Si chiede di modellare il sistema utilizzando
LTSA e di scrivere il corrispondente programma Java con thread e monitor.
Per LTSA: limitare a due il numero dei
distributori; definire la capacità dei distributori come una costante di valore 5; limitare a tre il numero dei clienti; definire i possibili valori
delle richieste dei clienti come range Q = 1..3.
Per Java: il numero dei clienti e il numero dei distributori devono essere parametri del
programma; la capacità dei distributori e il valore massimo delle richieste da parte dei clienti vanno dichiarati come costanti.
Affrontare il problema Affrontare il problema
1) Capire il problema
¾ Individuare le entità
¾ Individuare gli aspetti di concorrenza
2) Costruire il modello
¾ Definire i processi
¾ Comporre i processi
¾ Proprietà?
3) Dal modello all’implementazione Java
Capire il problema Capire il problema
Esaminando il testo si possono individuare tre entità:
¾ i clienti
¾ i distributori
¾ l'autobotte
Esaminiamo il problema posto per
individuare dove c'e' concorrenza
Il problema: la concorrenza Il problema: la concorrenza
I clienti – attivi (saranno processi in Java)
¾ accedono ai distributori che sono le risorse condivise:
qui serve la mutua esclusione
I distributori – passivi (saranno monitor)
¾ poiche' i clienti scelgono a priori, non occorre una gestione integrata
¾ servono tanti monitor quanti sono i distributori (quindi basta definire un processo ed istanziarlo n volte)
L'autobotte - attiva
¾ Il testo dice: "il rifornimento può avvenire solo se i distributori sono liberi, e durante il rifornimento i clienti non possono accedere a nessun distributore".
¾ Questa e' la condizione di eventuale sospensione dei clienti all'interno del monitor (il distributore e' libero, ma inaccessibile per rifornimento).
Il Il modello modello
Costanti e intervalli
const Clienti = 3
// numero max clienti const CapienzaMax = 3
// capienza max del distributore range Capienza = 0..CapienzaMax
// capienza del distributore range NumDistributori = 1..2
// numero e identificatori dei distributori range Richiesta = 1..2
// carburante richiesto dai clienti
Il modello: CLIENTE Il modello: CLIENTE
Un cliente sceglie un distributore d ed effettua una richiesta r
CLIENTE = (
dist[d:NumDistributori].chiedi[r:Richiesta] ->
C[d][r] ),
se c'e' benzina sufficiente "carica", altrimenti "rinunzia"
C[d:NumDistributori][r:Richiesta] = ( dist[d].carica -> CLIENTE |
dist[d].rinunzia -> CLIENTE ).
(la composizione con il distributore
determina l'azione possibile)
Il modello: AUTOBOTTE Il modello: AUTOBOTTE
Supponiamo che tutti i distributori
vengano riempiti con un'unica operazione di "rifornimento"
AUTOBOTTE = ( rifornimento -> AUTOBOTTE ).
(L'azione verra' abilitata solo quando non ci sono clienti che stanno usando uno o
piu' distributori)
Il modello: DISTRIBUTORE Il modello: DISTRIBUTORE
Per modellare un distributore basta una
etichetta che rappresenti la benzina presente
la mutua esclusione viene modellata dalla sequenza delle azioni.
DISTRIBUTORE = DISTRIBUTORE[CapienzaMax], DISTRIBUTORE[disponibile:Capienza] = (
chiedi[r:Richiesta] -> SERVIZIO[disponibile][r] | rifornimento -> DISTRIBUTORE[CapienzaMax] ),
un distributore può accettare o la richiesta di un cliente, o la richiesta dell'autobotte
Il modello: DISTRIBUTORE Il modello: DISTRIBUTORE
attivando un sottoprocesso per servire un cliente si esclude la possibilita' del
rifornimento, come richiesto dal testo.
SERVIZIO[disponibile:Capienza][r:Richiesta] = ( when ( r <= disponibile ) carica ->
DISTRIBUTORE[disponibile-r] | when ( disponibile < r ) rinunzia ->
DISTRIBUTORE[disponibile] ).
NOTA: il rifornimento e' stato modellato con una sola operazione, se fosse stato modellato con piu' operazioni si sarebbe potuto creare un nuovo sottoprocesso come per SERVIZIO.
Il modello: il sistema Il modello: il sistema
||CMP = ( cliente[1..Clienti]:CLIENTE ||
{cliente[1..Clienti]}::
dist[d:NumDistributori]:DISTRIBUTORE ||
AUTOBOTTE )
/ { rifornimento / {
cliente[1..Clienti].dist[d:NumDistributori].rifornime nto } }.
Le operazioni "rifornimento" vengono
unificate per modellare il fatto che solo l'autobotte puo' invocare l'azione,
altrimenti anche i clienti potrebbero
farlo
L L ’ ’ implementazione implementazione
Il sistema implementa come classi i
processi del modello e le istanzia in modo opportuno
Aspetti rilevanti
¾ Clienti accedono al distributore: basta la mutua esclusione -> synchronized
¾ Autobotte accede al distributore: bisogna fermare tutti i clienti -> wait & notify
Accesso ai dati condivisi Accesso ai dati condivisi
Nel nostro contesto sono rilevanti i dati condivisi
2 situazioni standard
Monitor
Cliente1 Cliente2
Attesa fuori dal monitor
Monitor
Cliente1 Cliente2
Attesa nel monitor
acquisisci rilascia
Il Il main main
public class Soluzione0110
{ public static void main (String[] args)
{ int numClienti = 0; // numero clienti (parametro)
int numDistributori = 0; // numero distributori (parametro)
// NOTA: se viene chiesto di parametrizzare un sistema, si devono // passare gli argomenti al main, come fatto in questa soluzione.
if (args.length < 2) {
System.out.println("Usage: java Soluzione0110 numDistributori numClienti");
return;
}
numDistributori = Integer.parseInt(args[0]);
numClienti = Integer.parseInt(args[1]);
System.out.println("Starting App");
Il Il main main
// creazione distributori
Distributore[] dist = new Distributore[numDistributori];
for ( int i=0 ; i < dist.length ; i++ ) {
dist[i] = new Distributore("dist" + (i+1));
}
// creazione dei clienti
for ( int i=1 ; i <= numClienti ; i++ ) {
new Cliente ("cliente" + i, dist).start();
}
// coerentemente al modello creo una cisterna new Autobotte(dist).start();
} }
Il Cliente Il Cliente
public class Cliente extends Thread {
static final int max = 3; // max richiesta per cliente Distributore[] dist;
public Cliente (String nome, Distributore[] dist) { super(nome);
this.dist = dist;
}
public void run () { }
}
Il Cliente Il Cliente
public void run () {
int qualed, quanto; //distributore scelto e richiesta
while (true) {
qualed = (int) (Math.random() * dist.length);
quanto = (int) (Math.random() * max + 1);
if ( dist[qualed].carica(quanto) )
L L ’ ’ Autobotte Autobotte
class Autobotte extends Thread { Distributore[] dist;
public Autobotte (Distributore[] d) { this.dist = d; } public void run () {
while (true) { try {
Thread.sleep((int) (Math.random()*3000));
} catch (InterruptedException e) { } // richiedo i distributori
for(int i=0; i<dist.length; i++) { dist[i].acquisisci();
}// li rifornisco
for(int i=0; i<dist.length; i++) { dist[i].rifornimento();
}// li rilascio
for(int i=0; i<dist.length; i++) { dist[i].rilascia();
} } } }
Il Il Distributore Distributore
class Distributore {
public final String nome;
final int pieno = 7;
int capacita = pieno;
boolean libero = true;
public Distributore (String nome) { this.nome = nome;
}
public synchronized boolean carica (int richiesta) { while (!libero) // se c'e' rifornimento aspetto
try {wait();} catch (InterruptedException e) { } if ( capacita < richiesta ) {
return false;
} else {
capacita -= richiesta;
return true;
} }
Il Il Distributore Distributore (cont.) (cont.)
public synchronized void acquisisci () {
// non ho bisogno di test: se entro vuol dire che non ci sono clienti // domanda: perche'?
libero = false;
}
public synchronized void rifornimento () {
System.out.println("Rifornimento " + nome);
capacita = pieno;
}
public synchronized void rilascia () {
// sveglio eventuali clienti in attesa libero = true;
notifyAll();
// domanda: si potrebbe usare solo notify()?
} }