Java offre deux façons de créer des thread :
La classe Thread déclare une méthode abstraite (une méthode abstraite est une méthode sans implémentation (ou sans corps). L'implémentation est donnée en écrivant le corps de la méthode dans une sous-classe) run() dans laquelle doit être rédigé le programme du thread. Dans l'exemple qui suit, nous créons deux processus qui vont tour à tour afficher des chaînes de caractères :
class A extends Thread { public void run () { for (int i = 0; i < 8; i++) { System.out.print ("A" + i + " "); try sleep (100); catch (InterruptedException e) {} } } } class B extends Thread { public void run () { for (int i = 0; i < 8; i++) { System.out.print ("B" + i + " "); try sleep (200); catch (InterruptedException e) {} } } } public class TestThread1 { static public void main (String args[]) { new A ().start (); new B ().start (); } }
Dans l'exemple, la vitesse d'exécution des threads est modifiée au moyen de la méthode sleep(int t) qui immobilise un processus durant t millisecondes. De cette modification, il résulte un entrelacement des impressions exécutées par les threads issus des classes A et B. Par exemple :
A0 B0 A1 B1 A2 A3 B2 A4 A5 B3 A6 A7 B4 B5 B6 B7
Nous pouvons remarquer que les classes A et B ont une écriture très proche. La programmation objet va permettre de factoriser ces deux écritures en une seule et pour un résultat équivalent :
class PrintingThread extends Thread { protected String name; protected int periode; public PrintingThread (String name, int periode) { this.name = name; this.periode = periode; } public void run () { for (int i = 0; i < 8; i++) { System.out.print (name + i + " "); try sleep (periode); catch (InterruptedException e) {} } } } public class TestThread2 { static public void main (String args[]) { new PrintingThread ().start ("A", 100); new PrintingThread ().start ("B", 200); } }
Dans la section précédente, nous avons vu qu'il était possible de demander au système de réaliser plusieurs traitement en même temps. Toutefois, il existe un inconvénient par rapport à cette capacité. Supposons que deux threads veulent accèder à la même imprimante pour y envoyer chacun un fichier. Si ces threads accèdent en même temps à cette imprimante, le résultat sera totalement illisible puisque certaines lignes, certains mots ou certains caractères des deux fichiers risquent de se mélanger avec ceux de l'autre fichier. Une stratégie plus sure consisterait à donner la priorité à l'un des deux threads et à mettre l'autre en attente de la fin d'impression. Cette technique qui n'autorise qu'un nombre limité de thread pour une ressource donnée permet d'assurer l'exclusion mutuelle pour cette ressource.
Java offre la primitive synchronized pour assurer l'exclusion mutuelle sur des objets par l'intermédiaire de leurs méthodes. Cependant, elle n'est pas suffisantes pour résoudre les principaux problèmes de la synchronisation de thread. Pour palier ce manque, nous devons la primitive de synchronisation pour élaborer des structures plus robustes comme les sémaphores et les moniteurs à la Hoare.
/* * Semaphore.java - implémentation des sémaphores en Java * */ public class Semaphore { private int counter; public Semaphore () { counter = 0; } public Semaphore (int i) { if (i < 0) throw new IllegalArgumentException (i + " < 0"); counter = i; } public synchronized void v () { if (counter == 0) this.notify (); counter++; } public synchronized void p () { while (counter == 0) this.wait (); counter--; } }
public interface Ressource { public void produire (int qtt); public void consommer (int qtt); }
Copyright © 2002-2005 - François Sarradin