IndexPage suivante

8.1- Les Threads

Définition

Java permet de créer des applications multitâches via les threads. Un thread est un sous-processus pouvant s'exécuter en parallèle avec d'autres threads et pouvant utiliser des ressources communes. Nous utilisons intensivement les threads lors de l'exécution d'une classe : le Garbage Collector qui vient nettoyer la mémoire s'exécute par exemple au sein d'un thread séparé. Bien entendu, les threads ne s'exécutent pas réellement en parallèle à moins de disposer de plusieurs processeurs. L'illusion du parallélisme est donné par l'exécution fragmentée des programmes.

Créer un thread

Il y a deux façons de créer un thread:

Lancement des threads

Le thread sera lancé par la méthode start() appliquée à l'instance de Thread concernée.

Sleep / wait / notify

Un thread se met en arrêt temporaire via la méthode statique sleep() de Thread. Attention, cette méthode est statique et ne peut être appelée de l'extérieur du thread pour le forcer au sommeil: elle s'appelle dans le thread lui-même. Le thread comportera alors un appel de type Thread.sleep([temps en ms]);

De la même façon, un thread peut se mettre en attente d'un événement par la méthode wait() qui doit être appelée dans la classe même. Dans ce cas, le thread attendra une notification de reprise d'activité pour reprendre. Un appel de notify() ( classe Object ) sur une instance de Thread permet de notifier à ce dernier de reprendre son exécution bloquée par un wait(). Un notifyAll() permet cette action sur tous les threads en attente.

Attention: Les méthodes sleep() et wait() peuvent provoquer des InterruptedException.


Variables modifiées en concurrence

Des"verrous" peuvent être posés sur des blocs d'instruction ou des méthodes d'une classe afin d'éviter les états indéterminés en cas d'accès concurrents. Il faut poser le mot-clé synchronized sur une méthode ou un bloc d'instructions qui accède aux données suceptibles d'être modifiées par plusieurs threads en même temps. Un wait() ne peut par exemple se faire que dans un bloc synchronisé.

Tableau récapitulatif

méthode

action

appliquée...

class Thread
void Thread.start() 

méthode d'instance


Démarre la méthode run() du thread concerné.


De l'extérieur du Thread.

class Thread
static void sleep(int)

méthode de classe


Le thread passe en sommeil le temps spécifié en ms

Dans le thread.

class Object
void wait()

méthode d'instance


Le thread courant passe en attente d'un notify

Dans le thread.

class Object
void notifyAll()

méthode d'instance

Notifie à tous les threads en attente de reprendre leur exécution

De l'extérieur du thread

class Object
void notify()

méthode d'instance


Notifie au thread sur lequel cette méthode est appliquée de reprendre son exécution.

De l'extérieur du thread

class Thread
static void yield()

méthode de classe


Force le thread courant à se met en pause et laisse la main au threads concurrents

Dans le thread

class Thread
void setPriority(int)

méthode d'instance


Fixe la priorité du thread. Souvent les valeurs:

Thread.MIN_PRIORITY, Thread.NORM_PRIORITY ou Thread.MAX_PRIORITY

De l'extérieur

class Thread
boolean isAlive()

méthode d'instance


Teste si le thread est vivant

De l'extérieur

class Thread
boolean isInterupted()

méthode d'instance


Teste si le thread est dans un wait()

De l'extérieur


Auto-démarrage d'un thread

Les actions extérieures au thread ne sont pas forcement gérées par une classe séparée. En effet, l'interface Runnable donne le moyen simple à un thread de se lancer lui-même. Pour cela, il suffit au thread d'implementer Runnable puis de déclarer une variable d'instance de type Thread instancié de cette façon:

Thread tThread=new Thread(this);

En donnant un argument de type Runnable au constructeur de Thread puis en lançant tThread.start(), la méthode run() de notre thread sera exécutée à travers tThread. Ceci n'est vrai que si nous donnons un type Runnable au constructeur de tThread. Dans le cas contraire, ce sera la méthode run() de tThread qui sera exécutée lors d'un tThread.start() .

Exemple d'application multi-thread

Dans cet exemple très simple, nous mettons en place une classe TestThread qui lance deux threads concurrents. Ces deux threads accèdent en concurrence à une ressource de la classe Ressource. L'un des threads incrémente la ressource et l'autre la décremente. Chaque thread va ensuite s'endormir pour une durée aléatoire.

ClasseTestThread

public class TestThread implements Runnable{
   boolean bInc;   / si vrai, le thread décremente la ressource, sinon, il l'incrémente
   Thread tThread=new Thread(this);
   Ressource rRessource;
   TestThread(Ressource rRessource,boolean bInc){ /*le constructeur prend en argument la ressource partagée*/
      this.rRessource=rRessource;
      this.bInc=bInc;
      tThread.start();//démarre le thread dans lequel nous sommes et non tThread
   }
   public void run(){
      System.out.println("Thread "+this +" started"); //affiche le no du thread
      while(true){
         try{
            if (bInc){
               rRessource.inc(); //incrémente la ressource
               Thread.sleep((int)(Math.random()*1000)); //dort jusqu'à 1 sec
            }
            else{
               rRessource.dec(); //décremente la ressource
               Thread.sleep((int)(Math.random()*1000));
            }
         }
         catch(InterruptedException ie){
            ie.printStackTrace();
         }
      }
   }
   public static void main(String[] sArgs){
      Ressource rRessource=new Ressource(10);
      TestThread tt1=new TestThread(rRessource,true);
      TestThread tt2=new TestThread(rRessource,false);
   }
}

ClasseRessource

class Ressource{
   int iComp; //la ressource elle-même : un compteur
   public Ressource(int iValue){
       this.iComp=iValue;
   }
   public synchronized void inc(){ //incremente la ressource de façon synchronisée
       iComp++;
       System.out.println(iComp);
   }
   public synchronized void dec(){ //décremente la ressource de façon synchronisée
       iComp--;
       System.out.println(iComp);
   }
}


IndexPage suivante