ABCSITE

INFORMATIQUE

 
 
 

 
 

 Java 

Les threads en Java

 

Nous allons dans ce nouveau chapitre, étudier les différentes manières qui nous sont proposées pour créer une application dite multi-thread (comportant plusieurs sous-processus). En fait, il y a deux possibilités que nous allons voir tout de suite.

La classe Thread.

Cette classe permet de pouvoir simplement créer un nouveau processus. Le plus simple est donc de sous classer cette classe. Cela impose une chose : il vous faut définir une fonction void run(). Celle ci contiendra le code qui devra s'exécuter dans le processus. A partir de là chaque nouvel objet que vous définirez fonctionnera dans son propre thread (1 objet == 1 thread). Pour lancer l'exécution de la méthode run, on doit faire un appel à la méthode start. Pour stopper le thread on peut appeler la méthode stop. Voici un petit exemple.

public class Parallele extends Thread {
String ch;
public Parallele(String ch) { this.ch=ch; start(); }
public void run() {
for(int i=0;i<500;i++) {
System.out.println(ch);
try { Thread.sleep(30); }
catch (InterruptedException e) {}
}
}

static public void main(String argv[]) {
Parallele p1 = new Parallele("Toto");
Parallele p2 = new Parallele("Tata");
while(true);
}


A titre indicatif, le sleep sert principalement de temporisateur. Mais il a aussi un rôle détourné : sur certaines architectures, le multi-treading ne fonctionne pas complètement. Le fait de faire un appel à sleep endort momentanément le processus et permet alors de donner la main à un autre processus : ces peut-être curieux, mais il est recommandé de procéder ainsi.

L'interface Runnable.

Cette interface vous demande d'implémenter un unique fonction : la fonction run. La classe abstraite Thread implémente donc (quoi que !!) cette interface. La différence réside dans le fait qu'un même objet de cette classe va pouvoir lancer plusieurs threads sur la même méthode run. Voici un exemple.

 

public class Parallele implements Runnable {
private int attr;
public Parallele() {
for (int i=0;i<5;i++)
(new Thread(this)).start(); 
}

public void run() {
for(int i=0;i<500;i++) {
System.out.println(Essai);
try { Thread.sleep(30); }
catch (InterruptedException e) {}
}
}

static public void main(String argv[]) {
Parallele p1 = new Parallele();
while(true);
}
}


Dans cet exemple tous les threads se partagent la même variable attr. Ce n'était pas le cas de la variable ch dans l'exemple précédent.

Quelle solution choisir ?

Il est clair que ce sont le besoins de votre programmes qui vont influencer votre choix. Si vous n'avez pas besoins de partager des données on peut choisir de dériver la classe Thread. Sinon, on peut implémenter l'interface Runnable (quoi qu'il suffirait peut-être de mettre le ressource à partager en static).

La synchronisation

Il est clair que selon les situations, on doit avoir des mécanismes de synchronisation pour permettre des accès concurrents à une ressource. Par exemple, seule un appel de méthode peut accéder à un même instant à une ressource, pour une modification. Il faut donc alors, rendre ces sections de code indivisible. Les concepteurs du langage Java ont donc choisit la notion de moniteurs pour arriver à solutionner le problème.

On utilise ces moniteurs d'une façon très simple. Il suffit de placer le mot clé synchronized dans la déclaration d'une méthode. Dès lors, un seul thread pourra exécuter la méthode à un même instant. Si un autre essaye, il sera endormi. Si une ressource fait défaut, vous pouvez alors aussi endormir le thread qui traite la méthode. Il nous faut aussi pouvoir réveiller un (ou plusieurs) processus afin que l'on ne tombe pas dans une situation d'interblocage. Pour ce faire, on peut utiliser deux méthodes void notify() et void notifyAll. L'exemple suivant reprend plus simplement les choses.

 

// ... du code
Ressource r = new Ressource();
public synchronized void get() {
try {
while(r.free()) wait(); /* Bonne nuit */
r.get();
notifyAll(); /* On réveille tout le monde */
}
catch (Exception e) {
// ....
}
}

 

 
 

ABCSITE © copyright 2002