Les processus légers "(threads)", ou fils d'exécution, permettent l'exécution de plusieurs tâches en même temps.
Qu'est ce qu'un processus léger ?
Un processus léger est un contexte d'exécution d'une application.
Ce processus possède sa propre pile et pointeur d'exécution.
Une application en cours d'exécution (un processus) peut avoir plusieurs sous-processus léger.
Tous les processus légers d'un même processus partagent la même zone de données.
Ce qui veut dire que toute variable membre d'une classe est modifiable par n'importe quel processus léger.
Il faut donc un moyen de synchroniser l'accès aux variables (voir paragraphe "Synchronisation").
Par défaut, une application possède un seul processus léger, créé par le système.
Cependant, en Java, d'autres processus légers sont créés quand l'application utilise une interface graphique, notamment un processus léger gérant la boucle de lecture des messages systèmes.
Processus léger courant.
En Java, tout processus léger est représenté par un objet de classe codice_1.
Le processus léger courant est retourné par la méthode statique codice_2 de la classe codice_1.
La classe codice_1 possède quelques méthodes statiques agissant sur le processus léger courant :
Créer un processus léger.
La classe codice_1 peut être dérivée pour créer un autre processus léger.
Dans ce cas, il faut surcharger la méthode codice_10 pour y mettre le code exécuté par le processus léger.
Exemple :
public class MyThread extends Thread
 public void run()
 try
 System.out.println("Un nouveau processus léger");
 Thread.sleep(1000); // suspendu pendant 1 seconde
 System.out.println("Fin du nouveau processus léger");
 catch(InterruptedException ex)
 System.out.println("Processus léger interrompu");
Il est alors créé et démarré de la manière suivante :
MyThread myth=new MyThread();
System.err.println("Démarrer le processus léger ...");
myth.start();
System.err.println("Le processus léger est démarré.");
Il n'est pas toujours possible d'étendre la classe codice_1 car Java n'autorise qu'une classe de base.
Mais il est permis d'utiliser plusieurs interfaces.
L'interface codice_12 permet de résoudre le problème.
Par défaut, la méthode codice_10 de la classe codice_1 appelle la méthode codice_10 de l'interface codice_12 passé en paramètre du constructeur.
Exemple :
public class MyClass extends AnotherClass
implements Runnable
 public void run()
 try
 System.out.println("Un nouveau processus léger");
 Thread.sleep(1000); // suspendu pendant 1 seconde
 System.out.println("Fin du nouveau processus léger");
 catch(InterruptedException ex)
 System.out.println("Processus léger interrompu");
Le processus léger est alors créé et démarré de la manière suivante :
MyClass myclass=new MyClass ();
Thread th=new Thread(myclass); // <-- processus léger créé
System.err.println("Démarrer le processus léger ...");
th.start();
System.err.println("Le processus léger est démarré.");
Actions sur un processus léger.
Cycle de vie d'un processus léger.
Un processus léger possède différents états gérés par le système :
InterruptedException.
Cette classe d'exception est lancée par les méthodes de la classe codice_1 et celle de la classe codice_18 demandant la suspension pour un temps indéterminé du processus léger courant (attente en général).
Cette exception est lancée quand le processus léger en attente est interrompu.
Capturer cette exception permet d'interrompre l'attente, et libérer des ressources pour terminer proprement.
Attendre la fin d'un processus léger.
La méthode codice_19 de la classe codice_1 peut être appelée pour attendre la fin d'un processus léger.
Exemple :
th.join(); // InterruptedException à capturer
Interrompre un processus léger.
La méthode codice_21 de la classe codice_1 peut être appelée pour interrompre un processus léger.
Cette méthode provoque le lancement d'une exception de type codice_23 quand le processus appelle une méthode d'attente.
Synchronisation.
La synchronisation devient nécessaire quand plusieurs processus légers accèdent aux mêmes objets.
mot-clé synchronized.
Le mot-clé codice_24 permet un accès exclusif à un objet.
La syntaxe est la suivante :
... code non protégé ...
synchronized(objet)
 ... code protégé ...
... code non protégé ...
Le code protégé n'est exécuté que par un seul processus léger à la fois, tant qu'il n'a pas terminé le bloc d'instruction.
Durant l'exécution de ce code protégé par un processus léger, un autre processus léger ne peut exécuter celui-ci, mais peut exécuter un autre bloc codice_24 si celui-ci n'utilise pas le même objet et qu'il n'est pas déjà en cours d'exécution.
Le mot-clé codice_24 peut également être utilisé dans la déclaration des méthodes :
public synchronized void codeProtege()
 ... code protégé ...
est équivalent à :
public void codeProtege()
 synchronized(this)
 ... code protégé ...
Pour une méthode statique (méthode de classe) :
public class MyClass
 public synchronized static void codeProtege()
 ... code protégé ...
est équivalent à :
public class MyClass
 public static void codeProtege()
 synchronized(MyClass.class)
 ... code protégé ...
Attente et signal.
Quand le mot-clé codice_24 ne suffit pas (par exemple, permettre l'accès à deux processus légers simultanément au lieu d'un seul), il est possible de suspendre un processus léger et le réveiller.
La classe codice_18 possède les méthodes suivantes :
Pour appeler l'une de ces quatre méthodes, il faut "posséder" l'objet.
Ce qui signifie utiliser l'instruction codice_24.
Dans le cas contraire, l'exception suivante est levée :
 java.lang.IllegalMonitorStateException: current thread not owner
Exemple :
synchronized(myobj)
 myobj.wait();
Comme ces méthodes sont définies dans la classe codice_18, il est possible de les utiliser avec n'importe quel type d'objet, et donc les chaînes de caractères et les tableaux.
Une bibliothèque spécialisée.
Il est très courant d'être amené à protéger l'accès à une variable pour juste par exemple, une incrémentation sous condition, comparer et échanger deux valeurs, chercher et ajouter. Ces méthodes sont qualifiées d'atomiques. La solution la plus efficace consiste à utiliser les classes de la bibliothèque java.util.concurrent. Ces classes disposent de méthodes considérées comme toujours plus rapides en mode multi-thread que celles d'une liste synchronisée, prenons par exemple le cas des listes :
String str;
Map<String,String> map=new HashMap<String,String>();
map.put("toto", "valeur exemple 1");
map.put("titi", "valeur exemple 1");
synchronized(map)
 if (!map.containsKey("titi"))
 map.put("titi", "valeur exemple 3");
Un autre thread ne peut pas effectuer de modification entre containsKey et put, du fait du verrou pris par l'instruction synchronized sur l'objet map. Mais cette solution est en réalité peu efficace car elle contraint le plus souvent à protéger tous les accès à la liste, en lecture comme en écriture, alors que les accès en lectures multiples n'ont pas à être bloquants, seuls ceux en écriture devant l'être. La classe ConccurentHashMap possède un putIfAbsent. Il est possible de réimplanter cette caractéristique via la synchronisation sur deux valeurs, mais les classes fournies par java.util.concurrent sont, elles, exemptes de bugs.
Si la classe de liste ne vous satisfait pas, la pose de verrou via la classe ReentrantReadWriteLock est extrêmement simple.