1. Multithreading
Si tratta della possibilità di eseguire parti
dell’applicazione in parallelo sfruttando le
caratteristiche e le potenzialità dei sistemi
operativi multithreading.
2. La classe Thread
Appartiene al package java.lang, quello base che
non deve essere importato.
Può essere usata per realizzare una
temporizzazione dei programmi utilizzando il suo
metodo statico sleep(millis):
Thread.sleep(2000);
Ma anche per realizzare classi che la estendono
e che permettono in tal modo dei realizzare un
Thread, ovvero un ‘processo’ parallelo.
class MiaClasse extends Thread
3. Overriding di run
L’esecuzione di un Thread presuppone l’esistenza di un operazione di
una certa durata caratterizzata solitamente da un ciclo ripetuto diverse
volte.
class MiaClasse extends Thread
{
public void run()
{
while(true)
{
//…… ciclo infinito
}
}
}
Il metodo run non viene mai invocato direttamente, ma per attivare il
thread è necessario usare il metodo start();
MiaClasse my=new MiaClasse();
my.start();
4. Thread.sleep
Causa la sospensione temporanea del Thread corrente.
Il thread ritornerà attivo dopo il numero di millisecondi indicato
try
{sleep(1000);}
catch(InterruptedException e){}
Il metodo sleep deve essere invocato in un blocco try catch perché
lancia l’eccezione InterruptedException.
5. Ciclo di vita di un Thread
nuovo
vivo
start
sospeso
Termine dello slot
temporale o sleep
wait
Termine dell’attesa
notify
morto
6. L’interfaccia Runnable
Si tratta di un interfaccia creata per permettere di implementare dei
Thread anche a partire da classi che già estendono un’altra classe.
class MiaFinestra extends Frame implements Runnable
{
public void run()
{
while(…)
{
}
}
public void altroMetodo()
{
Thread runner=new Thread(this);
runner.start();
}
}
7. Priorità dei Thread
La maggior parte dei sistemi operativi è in grado di gestire i
Thread con diversi livelli di priorità.
Java dispone di 10 livelli di priorità, di questi non tutti sono
realmente differenti nell’utilizzo pratico, perché i sistemi operativi
comunemente supportano meno di 10 livelli. Solitamente
comunque si usano i tre livelli indicati dalle tre costanti:
MIN_PRIORITY
NORM_PRIORITY
MAX_PRIORITY
Il metodo che permette di settare la priorità è
setPriority(n);
8. Thread Concorrenti
Uno dei problemi più rilevanti nello sviluppo di applicazioni Multithreading
è la possibile concorrenza dei thread su dati comuni.
Il problema viene risolto generalmente tramite l’utilizzo di strutture dati
per loro natura sincronizzate (Vector, Hashtable) oppure sincronizzando
forzatamente tramite la parola chiave synchronized i metodi
potenzialmente pericolosi in tal senso.
public synchronized void eseguiTransazione(){….}
Il metodo sincronizzato verrà richiamato dal metodo run dei Thread, ma
non sarà un methodo del Thread. La sincronizzazione impedisce che il
metodo venga interrotto durante la sua esecuzione. In pratica solo un
Thread alla volta può usare un metodo sincronizzato. L’oggetto su cui il
Thread lavora rimane quindi bloccato finché il metodo sincronizzato non è
terminato.
9. Blocchi sincronizzati
In alcuni casi non è necessario sincronizzare un intero metodo perché la parte
critica è solo una porzione del codice di un metodo:
La sincronizzazione della porzione critica avviene in questo modo:
public void esegui()
{
// sezione non critica
synchronized (this)
{
// sezione critica
}
// sezione non critica
}
(La parte sincronizzata di un oggetto viene chiamata Monitor, si dice anche che
un Thread ha un ‘lock’ sul monitor di un oggetto)
10. wait e notifyAll
L’uso dei metodi wait() e notifyAll() (in un blocco sincronizzato),
appartenenti alla classe Object (non a Thread) permette di sospendere
l’esecuzione di un Thread fino al verificarsi di una determinata condizione.
while(conto[da]<importo) wait();
conto[da]-=importo;
conto[a]+=importo;
notifyAll();
Dalla situazione di sospensione il Thread tuttavia non si risveglierebbe
più se non venisse invocato il metodo notifyAll(). Questo metodo consente
che tutti i Thread rivalutino il loro stato ed eventualmente lascino lo stato di
attesa.
L’uso di wait, se mal progettato, potrebbe comunque provocare
situazioni di stallo che devono essere ben analizzate: potrebbe cioè
accadere che tutti i Thread si portino in stato di attesa senza quindi che
nessuno possa più risvegliarli.
(wait() lancia l’eccezione InterruptedException)