Verrou et Condition
En Java, la synchronisation standard repose sur le mot-clé synchronized et les objets moniteurs intégrés. Cependant, dans certains cas, synchronized peut s’avérer insuffisant, en particulier lorsque davantage de flexibilité dans la gestion des threads est requise.
Description générale
Les interfaces Lock et Condition, introduites dans le package java.util.concurrent.locks, offrent des fonctionnalités avancées de gestion des threads.
Sur cette illustration, il est possible de voir que le premier thread acquiert le verrou à l’aide de la méthode lock(), et à ce moment-là, un autre thread ne peut pas acquérir 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 acquérir le verrou.
Différence
La différence entre ces deux interfaces est que les implémentations de Lock constituent une alternative de haut niveau au bloc synchronized, tandis que 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 concrets
Imaginez que vous gérez une file d'attente pour l'inscription à un événement. Afin d'éviter tout dépassement et d'assurer une allocation correcte des places, il est nécessaire d'utiliser des mécanismes de blocage et des conditions pour maintenir le flux des nouvelles inscriptions en attente jusqu'à ce qu'une place 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 la gestion explicite des verrous.
Principales méthodes de ReentrantLock :
lock(): Acquisition d'un verrou ;unlock(): Libération du verrou ;tryLock(): Tente d'acquérir le verrou et retourne true si la capture réussit ;tryLock(long timeout, TimeUnit unit): Tente d'acquérir le verrou pendant la durée spécifiée ;newCondition(): Crée une condition pour leLockcourant.
Main.java
12345678910111213141516171819202122232425262728293031package 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 acquiert le verrou avec lock.lock(), puis exécute le code, et enfin, 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 précise : ce bloc est presque toujours exécuté, même en cas d’exception, sauf lors de la terminaison du programme.
Interface Condition
Nous pouvons uniquement créer un objet Condition en l’associant à 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.
Main.java
12private 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.
Main.java
12345678910111213141516private 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 son exécution et affiche le message « Condition remplie ! »
Lorsque la méthode await() est appelée, le thread est mis en pause et libère également le verrou qu'il a acquis. Lorsque le thread se réveille, il doit acquérir à nouveau le verrou et ce n'est qu'alors qu'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() acquiert le verrou avec lock.lock() afin d'empêcher plusieurs threads d'exécuter le code simultanément.
Condition avec Condition : S'il n'y a pas d'espaces disponibles, le thread appelle spaceAvailable.await() pour attendre qu'un espace se libère.
Libération du verrou : Lorsqu'un thread a libéré un espace en utilisant la méthode cancel(), il appelle spaceAvailable.signalAll() pour notifier tous les threads en attente.
Gestion des exceptions : L'utilisation de blocs try-finally garantit que le verrou est libéré même si une exception se produit.
L'utilisation de Lock et Condition en Java permet un contrôle plus flexible des threads et de la synchronisation que le mécanisme synchronized traditionnel. Ceci est particulièrement utile dans des scénarios complexes nécessitant un contrôle plus précis des threads et des conditions d'attente.
Merci pour vos commentaires !
Demandez à l'IA
Demandez à l'IA
Posez n'importe quelle question ou essayez l'une des questions suggérées pour commencer notre discussion
Awesome!
Completion rate improved to 3.33
Verrou et Condition
Glissez pour afficher le menu
En Java, la synchronisation standard repose sur le mot-clé synchronized et les objets moniteurs intégrés. Cependant, dans certains cas, synchronized peut s’avérer insuffisant, en particulier lorsque davantage de flexibilité dans la gestion des threads est requise.
Description générale
Les interfaces Lock et Condition, introduites dans le package java.util.concurrent.locks, offrent des fonctionnalités avancées de gestion des threads.
Sur cette illustration, il est possible de voir que le premier thread acquiert le verrou à l’aide de la méthode lock(), et à ce moment-là, un autre thread ne peut pas acquérir 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 acquérir le verrou.
Différence
La différence entre ces deux interfaces est que les implémentations de Lock constituent une alternative de haut niveau au bloc synchronized, tandis que 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 concrets
Imaginez que vous gérez une file d'attente pour l'inscription à un événement. Afin d'éviter tout dépassement et d'assurer une allocation correcte des places, il est nécessaire d'utiliser des mécanismes de blocage et des conditions pour maintenir le flux des nouvelles inscriptions en attente jusqu'à ce qu'une place 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 la gestion explicite des verrous.
Principales méthodes de ReentrantLock :
lock(): Acquisition d'un verrou ;unlock(): Libération du verrou ;tryLock(): Tente d'acquérir le verrou et retourne true si la capture réussit ;tryLock(long timeout, TimeUnit unit): Tente d'acquérir le verrou pendant la durée spécifiée ;newCondition(): Crée une condition pour leLockcourant.
Main.java
12345678910111213141516171819202122232425262728293031package 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 acquiert le verrou avec lock.lock(), puis exécute le code, et enfin, 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 précise : ce bloc est presque toujours exécuté, même en cas d’exception, sauf lors de la terminaison du programme.
Interface Condition
Nous pouvons uniquement créer un objet Condition en l’associant à 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.
Main.java
12private 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.
Main.java
12345678910111213141516private 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 son exécution et affiche le message « Condition remplie ! »
Lorsque la méthode await() est appelée, le thread est mis en pause et libère également le verrou qu'il a acquis. Lorsque le thread se réveille, il doit acquérir à nouveau le verrou et ce n'est qu'alors qu'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() acquiert le verrou avec lock.lock() afin d'empêcher plusieurs threads d'exécuter le code simultanément.
Condition avec Condition : S'il n'y a pas d'espaces disponibles, le thread appelle spaceAvailable.await() pour attendre qu'un espace se libère.
Libération du verrou : Lorsqu'un thread a libéré un espace en utilisant la méthode cancel(), il appelle spaceAvailable.signalAll() pour notifier tous les threads en attente.
Gestion des exceptions : L'utilisation de blocs try-finally garantit que le verrou est libéré même si une exception se produit.
L'utilisation de Lock et Condition en Java permet un contrôle plus flexible des threads et de la synchronisation que le mécanisme synchronized traditionnel. Ceci est particulièrement utile dans des scénarios complexes nécessitant un contrôle plus précis des threads et des conditions d'attente.
Merci pour vos commentaires !