Home
Home
Tutorials
   PHP
   Java
   Tutti...
Manuali
SmartImage
Marketing
Downloads
Contatti
Affiliati

  Da vedere
Hosting
Statistiche per siti
Corsi per webmaster
Hardware
Processori


  OnFocus
Sostituire parole o testi in una variabile

Meno codice più risultati con INSERT ... ON KEY DUPLICATE UPDATE

Contatore di accessi in PHP/MySQL

  Siti Amici
Miki News
Giochi gratis
Areagratis
Baratto Online
AI Machines
Guide e Manuali Gratis
ScambioLink.org
Suonerie
Reiki e Mantra
Abbassa la vista
Antivirus
Twago
Add to Technorati Favorites

Tutti i siti amici
Diventa Affiliato

 


Autore: Claudio Venturini
Categoria: java
Livello: normale Livello normale

Cenni di programmazione multi-threading - parte 2

Controllare l'interazione tra i thread - la mutua esclusione

Nella prima parte di questa serie di tutorial dedicati alla programmazione concorrente ho illustrato in cosa consistono i thread in Java e come crearli. Vediamo ora come sfruttarli. Nel caso del web server, che abbiamo preso a titolo di esempio nella prima parte, ho spiegato che si potrebbe utilizzare un nuovo thread per ogni richiesta da servire. E' ovvio che in questo caso i thread sono totalmente indipendenti, e non hanno bisogno di interagire.
Modifico leggermente l'esempio: supponiamo che il web server voglia contare anche il numero di pagine che ha servito; ci si trova di fronte a un problema di mutua esclusione. Questo tutorial è dedicato alla spiegazione e risoluzione di questo problema.

Il problema della mutua esclusione

Per contare le pagine servite dal nostro web server è necessario che ogni thread, dopo aver servito la richiesta per cui è stato creato, vada ad incrementare una variabile condivisa tra tutti i thread, che ha appunto la funzione di contare le richieste servite: la chiamerò countPages.
L'azione di incrementare una variabile è in realtà composta da tre istruzioni a livello macchina: la lettura del valore dalla memoria, la somma tra tale valore e 1 (cioè l'incremento vero e proprio), e la scrittura del nuovo valore in memoria. Ciò significa che questa azione non è atomica, ovvero non è indivisibile. Per spiegare meglio questo concetto utilizzo un esempio: consideriamo un thread t1 che sta eseguendo la prima delle tre istruzioni. Esso può essere interrotto dal sistema operativo, e il controllo può passare ad un altro thread t2, che magari legge a sua volta tale valore. Siamo nella condizione in cui entrambi i thread hanno letto il valore n di countPages. Ora ipotizziamo che il controllo torni al thread t1: esso esegue la somma n+1 e salva il nuovo valore nella memoria, ovvero nella variabile countPages. t1 termina e il controlla torna di nuovo a t2, che aveva letto il valore n, e quindi esegue n+1 e lo salva in countPages. Il risultato è che countPages contiene n+1, mentre dovrebbe contenere n+2, in quanto sia t1 che t2 hanno servito una richiesta.
Da qui nasce il problema della mutua esclusione: dobbiamo rendere atomica l'azione di incrementare countPages, ovvero non deve succedere che un thread t2 vada a lavorare sulla variabile countPages finchè è occupata da un thread t1.

I metodi synchronized

Fortunatamente Java ci rende la vita particolarmente semplice nel gestire questo tipo di situazioni. E' infatti possibile specificare la keyword synchronized nell'intestazione del metodo che fornisce l'accesso alla variabile countPages (deve esserci un metodo che accede a tale variabile, non specificate public la variabile!). In particolare creiamo una classe MyCounter che rappresenta il nostro contatore, contenente il metodo increase() per incrementare la variabile countPages.

public class MyCounter {
  private int countPages;

  public MyCounter(){
    countPages 0;
  }

  public synchronized int increase(){
    countPages++;
    return countPages;
  }
}

La Java Virtual Machine gestisce una coda per ogni oggetto che contiene metodi dichiarati synchronized. Se un thread t2 chiama il metodo increase() mentre lo sta già eseguendo un altro thread t1, esso viene inserito in coda. Quando t1 termina l'esecuzione del metodo viene prelevato il prossimo thread dalla coda, in questo caso t2. In questo modo abbiamo garantito la mutua esclusione, ovvero un solo thread per volta andrà a toccare la variabile countPages.

Modifichiamo quindi il codice della classe MyMultiThreadedProgram in modo che crei l'oggetto contatore e lo passi ai thread che vengono creati, altrimenti questi non potrebbero sapere che variabile devono incrementare:

public class MyMultiThreadedProgram {
  public static void main(String args[]){
    // creo il contatore
    MyCounter count = new MyCounter();
    // creo il mio thread
    Thread t = new MyThread(count);
    // attivo il thread
    t.start();
  }

Andiamo a modificare leggermente anche i nostri thread: in particolare la classe MyThread deve contenere un riferimento all'oggetto contatore:

public class MyThread extends Thread{
  private MyCounter countPages;

  public void run(){
    // azioni del thread...
    // incremento countPages
    countPages.increase();
  }

  public MyThread(MyCounter count){
    countPages count;
  }

Alla prossima puntata!

Cenni di programmazione multi-threading Precedente Indice Successivo Installare Java
Cenni di programmazione multi-threading Installare Java