Metodologia di progettazione
I problemi concorrenti e distribuiti sono spesso abbastanza complessi da capire e gestire. Serve allora un approccio sistematico per descrivere e ragionare su tali problemi, che faciliti la progettazione e la realizzazione di una soluzione.
La progettazione riguarda la creazione di collezioni di oggetti che si coordinano tra loro per risolvere un problema. Il problema può essere scomposto in oggetti di due tipi:
- gli oggetti attivi, che rappresentano il comportamento dei thread (ovvero conterranno il codice che verrà eseguito da ciascun thread);
- gli oggetti passivi, che:
- reagiscono alle richieste degli oggetti attivi (e non hanno, invece, un comportamento proprio: in assenza di richieste, rimangono completamente inattivi)
- contengono elementi di controllo degli oggetti attivi (semafori, metodi synchronized, ecc), per essere thread-safe, coordinare e sincronizzare thread, ecc..
La progettazione basata su questa suddivisione comporta i seguenti passi:
- scrivere una breve descrizione, tipicamente testuale e informale, del problema da risolvere
- descrivere il sistema con UML, identificando gli oggetti attivi, quelli passivi e le loro interazioni;
- implementare gli oggetti attivi come thead Java
- implementare gli oggetti passivi come monitor
- scrivere un oggetto di controllo (il “main”) che crea le istanze di tutti gli altri oggetti.
Esempio di specifiche testuali
Come esempio, si considera il problema del produttore-consumatore, le cui specifiche (già viste in precedenza) sono le seguenti:
- il produttore crea dati consistenti in numeri interi (in questo esempio di problema; in generale, può produrre dati di un tipo qualsiasi), e li memorizza in un buffer di capienza limitata.
- Il consumatore preleva dal buffer i numeri interi, e (in questo esempio) li visualizza sul dispositivo di output.
- Quando il consumatore preleva un dato, questo viene cancellato dal buffer, creando un posto vuoto per un ulteriore dato.
- Produttore e consumatore operano senza soluzione di continuità, cioè eseguono cicli di produzione e consumo potenzialmente infiniti.
- Il buffer la capacità di memorizzare $N$ elementi.
- Se il produttore produce un dato e il buffer è pieno, non può depositarlo immediatamente, ma deve aspettare che nel buffer ci sia almeno uno spazio liberi (che si creerà quando il consumatore consumerà un dato).
- Se il consumatore è pronto a recuperare un dato, ma il buffer è vuoto, deve aspettare che nel buffer ci sia almeno un dato (che dovrà essere depositato dal produttore).