28 settembre 2008

Thread Concorrenti in Java

In alcuni casi, quando ci si trova nella situazione in cui vengono eseguiti diversi task in sequenza, ciascuno dei quali è indipendente dagli altri, e si ha la necessità di dover ridurre i tempi di elaborazione, strutturare il coidice in maniera tale da eseguire thread paralleli può essere una soluzione.
Nel mio esempio si ha la necessità di eseguire 3 chiamate a WebServices, che come ben sapete sono chiamate sincrone (bloccano il processo chiamante fino ad una risposta o un failure), quindi il tempo totale di esecuzione è dato da :
n_task
∑ ExecTime(i)
i=0


Introducendo una strategia di gestione mediante thread paralleli si può ridurre il tempo di esecuzione al :
n_task
Max(ExecTime(i))
i=0


Supponiamo di modificare il codice di una classe ClassSample inserendo tra le proprietà le seguenti 3 dichiarazioni:
public Integer cont = new Integer(0);
public Integer sincronizzatore = new Integer(0);
public static final int NUM_THREAD = 3;


La variabile cont rappresenta il contatore dei thread che hanno già terminato la propria esecuzione.
La variabile sincronizzatore rappresenta l'oggetto che funge da sincronizzatore.
La variabile statica NUM_THREAD che rappresenta il numero di thread che verranno eseguiti (tale numero è statico ma può dipendere dal contesto)

A questo punto è necessario definire i Thread, nel mio esempio assumiamo che i thread siano delle chiamate a servizi Web che come ben sapete sono chiamate bloccanti :
class Servizio1Thread extends Thread { 
/***
* Metodo run per avviare il thread
*/
public void run() {
// Parte thread Ws
RisultatoServizio risultatoServizio = chiamoServizio(parametro1,parametro2);
controllaSemaforo();

}
}


Definiamo la funzione che controlla l'esecuzione di tutti i Threads
>/***
* Metodo controllore dei thread completati. Nel caso in cui
* siano stati completati tutti i thread invoca una notifica al thread
* principale che è temporaneamente in stato di wait
*/
public void controllaSemaforo (){
synchronized (sincronizzatore){
logger.debug("processi chiusi "+(cont.intValue()+1)+"/"+NUM_THREAD);
if (cont.intValue()+1<NUM_THREAD){
cont = new Integer(cont.intValue()+1);
}else{
logger.debug("notifico al thread principale");
sincronizzatore.notifyAll();
}
}
}


Ora passiamo all'implementazione del Thread principale, che si occuperà di lanciare i 3 Thread paralleli e attenderne il completamento prima di continuare con il normale flusso di esecuzione.
//Thread 1 
Servizio1Thread myFirstThread = new Servizio1Thread ();
myFirstThread.start();
//Thread 2
Servizio2Thread myRirThread = new Servizio1Thread ();
myRirThread.start();
//Thread 3
Servizio3Thread myWsThread = new Servizio1Thread ();
myWsThread.start();

try{

// blocco sincronizzato per attendere i risultati
synchronized(sincronizzatore) {
//Fermo il thread principale
sincronizzatore.wait();
// Hanno finito i thread che hanno sbloccato l'esecuzione del Thread principale
}
// qui continua il normale flusso di esecuzione
}catch(InterruptedException ex){
System.out.println("Eccezione : "+ex.getMessage());
}

Naturalmente si può giocare con le chiamate in maniera opportuna all'operazione che si intende eseguire. Spero che tale soluzione velocizzerà le vostre applicazioni. Alla prossima!

Nessun commento: