Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Apprendre Verrou et Condition | Mécanismes de Synchronisation de Haut Niveau
Multithreading en Java
course content

Contenu du cours

Multithreading en Java

Multithreading en Java

1. Notions de Base du Multithreading
2. Collections Synchronisées
3. Mécanismes de Synchronisation de Haut Niveau
4. Meilleures Pratiques de Multithreading

book
Verrou et Condition

En Java, la synchronisation standard est basée sur le mot-clé synchronized et les objets moniteurs intégrés. Cependant, dans certains cas, synchronized peut ne pas être suffisant, surtout lorsque plus de flexibilité dans la gestion des threads est requise.

Description Générale

L'interface Lock et l'interface Condition, introduites dans le package java.util.concurrent.locks, fournissent des capacités avancées de gestion des threads.

Dans cette image, vous pouvez voir que le premier thread capture le verrou en utilisant la méthode lock(), et à ce moment-là, un autre thread ne peut pas capturer le même verrou. Dès que tout le code à l'intérieur du verrou est exécuté, il appellera la méthode unlock() et libérera le verrou. Ce n'est qu'après cela que le deuxième thread pourra capturer le verrou.

Différence

La différence entre ces deux interfaces est que les implémentations de Lock sont une alternative de haut niveau au bloc synchronisé, et les implémentations de l'interface Condition sont une alternative aux méthodes notify()/wait(). Ces deux interfaces font partie du package java.util.concurrent.locks.

Exemples de la vie réelle

Imaginez que vous gérez une file d'attente pour l'inscription à un événement. Pour éviter le dépassement et assurer une allocation correcte des sièges, vous devez utiliser des mécanismes de blocage et des conditions pour maintenir le flux de nouvelles inscriptions en attente jusqu'à ce qu'un siège libre soit disponible.

Classe ReentrantLock

La classe ReentrantLock du package java.util.concurrent.locks est une implémentation de l'interface Lock. Elle fournit des fonctionnalités pour gérer explicitement les verrous.

Les principales méthodes de ReentrantLock :

  • lock(): Capture un verrou;
  • unlock(): Libère le verrou;
  • tryLock(): Tente de capturer le verrou et retourne vrai si la capture est réussie;
  • tryLock(long timeout, TimeUnit unit): Tente de capturer le verrou pendant le temps spécifié;
  • newCondition(): Crée une condition pour le Lock actuel.
java

Main

copy
12345678910111213141516171819202122232425262728293031
package com.example; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Main { // Creating a ReentrantLock object private final Lock lock = new ReentrantLock(); private int count = 0; // Method to increment the `count` variable public void increment() { lock.lock(); // Acquiring the `lock` try { count++; System.out.println("Count incremented to: " + count); } finally { lock.unlock(); // Releasing the `lock` } } public static void main(String[] args) { Main example = new Main(); // Runnable task to call the increment method Runnable task = example::increment; // Starting multiple threads to execute the task for (int i = 0; i < 5; i++) { new Thread(task).start(); } } }

Comme vous pouvez le voir, dans la méthode increment() nous utilisons le verrouillage avec Lock. Lorsqu'un thread entre dans la méthode, il capture le verrou avec lock.lock() puis exécute le code et ensuite dans le bloc finally nous libérons le verrou avec lock.unlock() afin que d'autres threads puissent entrer.

Nous libérons le verrou dans le bloc finally pour une raison, c'est simplement que ce bloc est presque toujours exécuté, même avec des exceptions, sauf lorsque nous terminons le programme.

Interface Condition

Nous pouvons seulement créer une Condition avec un lien vers une implémentation spécifique de Lock. Pour cette raison, les méthodes de Condition n'affecteront que le verrouillage de cette implémentation particulière de Lock.

java

Main

copy
12
private final Lock lock = new ReentrantLock(); private final Condition condition = lock.newCondition();

Les principales méthodes de Condition :

  • await(): Attend un signal d'un autre thread ;
  • signal(): Débloque un thread en attente sur une condition ;
  • signalAll(): Débloque tous les threads en attente sur la condition.
java

Main

copy
12345678910111213141516
private final ReentrantLock lock = new ReentrantLock(); private final Condition condition = lock.newCondition(); private boolean ready = false; public void waitForCondition() throws InterruptedException { lock.lock(); // Acquire the `lock` try { while (!ready) { // Check if the condition is not met System.out.println("Waiting..."); // Print a waiting message condition.await(); // Wait for the condition to be signaled } System.out.println("Condition met!"); // Print a message when the condition is met } finally { lock.unlock(); // Release the `lock` } }

La méthode waitForCondition() bloque le thread jusqu'à ce que la variable ready devienne true, ce qui indique que la condition a été remplie. Lorsque la condition est remplie, le thread continue de s'exécuter, affichant le message “Condition remplie !”

Remarque

Lorsque la méthode await() est appelée, le thread est suspendu et libère également le verrou qu'il a capturé. Lorsque le thread se réveille, il doit capturer le verrou à nouveau et seulement alors il commencera à s'exécuter !

Exemple de Code

Voyons maintenant un exemple d'utilisation de ReentrantLock et Condition pour gérer l'inscription à un événement :

Un court extrait de la vidéo

Verrouillage avec ReentrantLock : La méthode register() capture le verrou avec lock.lock() pour empêcher plusieurs threads d'exécuter le code en même temps.

Condition avec Condition : S'il n'y a pas d'espaces disponibles, le thread appelle spaceAvailable.await() pour attendre jusqu'à ce qu'un espace soit disponible.

Libérer le verrou : Lorsqu'un thread a libéré de l'espace en utilisant la méthode cancel(), il appelle spaceAvailable.signalAll() pour notifier tous les threads en attente.

Gestion des exceptions : L'utilisation des blocs try-finally garantit que le verrou est libéré même si une exception se produit.

Conclusion

L'utilisation de Lock et Condition en Java permet un contrôle plus flexible des threads et de la synchronisation que le mécanisme synchronisé traditionnel. Cela est particulièrement utile dans des scénarios complexes où un contrôle plus précis des threads et des conditions d'attente est requis.

Tout était clair ?

Comment pouvons-nous l'améliorer ?

Merci pour vos commentaires !

Section 3. Chapitre 1
We're sorry to hear that something went wrong. What happened?
some-alt