Un buon programmatore non può definirsi tale se non è in grado di destreggiarsi nella programmazione di software multi-threading, ovvero composto dall’esecuzione concorrente di più thread, dello stesso processo. In questo tutorial ci avvicineremo ai concetti base della programmazione concorrente in Java.
Cos’è un thread
Possiamo pensare un thread come un flusso di esecuzione. Il classico processo è un flusso di esecuzione di un dato codice, ovvero un insieme di istruzioni macchina. In un software basato sul multi-threading un processo è costituito da più thread, ovvero più flussi di esecuzione, che possono virtualmente procedere indipendentemente e parallelamente. A titolo esemplificativo pensiamo ad un web server, come Apache. Esso deve essere sempre in ascolto di nuove richieste e contemporaneamente eseguire quelle ricevute. Con la programmazione standard non è possibile ricevere nuove richieste fino a quando l’ultima richiesta ricevuta non è stata servita. Come è immaginabile molte delle richieste entranti andrebbero perse. Se però utilizziamo due thread, possiamo fare in modo che uno si dedichi a ricevere e accodare le richieste, e l’altro a servirle. Ovviamente ciò può realmente avvenire solo su macchine dotate di più processori, ma viene be simulato da ormai qualsiasi sistema operativo.
Un thread in Java
In Java un thread è rappresentato dalla classe Thread (con molta fantasia). Per creare un thread non dovremo fare altro che implementare una classe che estende la classe Thread; noi la chiameremo MyThread.
Bene, fin qui tutto ok. Ma dove metto il codice che il thread deve eseguire? E come faccio ad attivare il thread?
Ogni classe che estende la classe Thread deve contenere un metodo pubblico e void run(). All’interno di esso bisogna inserire il codice del nostro thread. Ovviamente essendo una classe a tutti gli effetti MyThread potrà contenere anche altri metodi, e anche un costruttore, poichè un thread in Java è ufficialmente un oggetto come altri… ma "eseguibile". Il metodo run() è paragonabile al metodo principale del processo main().
public class MyThread extends Thread {
public void run(){
System.out.println('Ciao, sono un thread');
}
public MyThread(){
// eventuali azioni per il costruttore
}
}
Per attivare il thread, ovvero avviare l’esecuzione del codice nel run(), è sufficiente chiamare il metodo start() della classe Thread, e quindi:
public class MyMultiThreadedProgram {
public static void main(String args[]){
// creo il mio thread
Thread t = new MyThread();
// attivo il thread
t.start();
}
}
Thread concorrenti
Quando viene mandato in esecuzione un qualsiasi programma Java, come sappiamo, viene eseguito il codice all’interno del metodo main(). In effetti il sistema operativo (o meglio la Java Virtual Machine) crea un processo a cui è associato un solo thread, che provvede ad eseguire il codice nel main(). Tutti i thread aggiuntivi che noi creiamo vengono associati allo stesso processo. Se creiamo un solo thread ne avremo quindi realmente in esecuzione due, uno da noi creato (nel nostro caso il thread t di tipo MyThread), e uno creato automaticamente all’avvio del programma, cioè il thread che esegue il main(). Questi due thread vengono eseguiti in modo parallelo a tutti gli effetti, come se fossero due processi distinti.
Come potete immaginare è quindi possibile creare ed eseguire quanti thread si desiderano. Possiamo modificare il nostro codice di esempio per eseguire due thread concorrenti:
public class MyMultiThreadedProgram {
public static void main(String args[]){
// creo il mio thread
Thread t1 = new MyThread();
Thread t2 = new MyThread();
// attivo il thread
t1.start();
t2.start();
}
}
I thread t1 e t2 vengono eseguiti in parallelo e possono quindi interagire tra loro o proseguire in modo totalmente indipendente.
Tornando all’esempio del web server potremmo usare un nuovo thread per ogni richiesta da servire, senza dover quindi utilizzare una coda delle richieste da servire.
E’ importante sapere inoltre che un programma multi-threaded termina solamente quando tutti i thread che lo compongono hanno terminato.