BlockingQueue et Ses Implémentations
Implémentations de base de BlockingQueue
Nous n'aborderons pas chaque implémentation en détail, car cela prendrait beaucoup de temps et il est peu probable que vous ayez besoin de toutes. Nous allons discuter des concepts généraux et des constructeurs disponibles.
Exemple concret
Imaginez une usine où un thread, le producteur, fabrique des pièces, et un autre thread, le consommateur, les traite. Le producteur place les pièces dans une file d'attente, tandis que le consommateur les récupère et les traite à partir de cette file. Si la file d'attente est vide, le consommateur attend que le producteur en ajoute de nouvelles. À l'inverse, si la file est pleine, le producteur attend que le consommateur libère de la place.
Un peu plus bas, nous allons implémenter cette tâche en code.
Différences avec d'autres types de collections
La BlockingQueue offre une synchronisation automatisée, gérant l'accès des threads à la file sans nécessiter de synchronisation manuelle. Elle prend également en charge des opérations bloquantes pour l'ajout et la récupération d'éléments, une fonctionnalité absente des autres collections comme ArrayList ou LinkedList.
Implémentations de BlockingQueue
ArrayBlockingQueue : Une file d'attente à taille limitée qui utilise un tableau pour stocker les éléments.
Main.java
123456789// Constructor with fixed capacity BlockingQueue<String> queue1 = new ArrayBlockingQueue<>(5); // Constructor with fixed capacity and fair access BlockingQueue<String> queue2 = new ArrayBlockingQueue<>(5, true); // Constructor with fixed capacity and initial collection of elements Collection<String> initialElements = java.util.Arrays.asList("One", "Two", "Three"); BlockingQueue<String> queue3 = new ArrayBlockingQueue<>(5, false, initialElements);
Le paramètre true active une politique d'accès équitable en fournissant un ordre FIFO pour l'accès des threads.
LinkedBlockingQueueue : Une file d'attente basée sur des nœuds chaînés pouvant être restreinte ou non restreinte.
Main.java
123456789// Constructor without capacity bounds BlockingQueue<String> queue1 = new LinkedBlockingQueue<>(); // Constructor with fixed capacity BlockingQueue<String> queue2 = new LinkedBlockingQueue<>(5); // Constructor with initial collection of elements Collection<String> initialElements = java.util.Arrays.asList("One", "Two", "Three"); BlockingQueue<String> queue3 = new LinkedBlockingQueue<>(initialElements);
PriorityBlockingQueue : Une file d'attente prioritaire non bornée où les éléments sont extraits selon leur ordre naturel ou tel que défini par un comparateur.
Main.java
12345678910111213// Constructor without initial capacity (default is 11) BlockingQueue<Integer> queue1 = new PriorityBlockingQueue<>(); // Constructor with initial capacity BlockingQueue<Integer> queue2 = new PriorityBlockingQueue<>(5); // Constructor with initial capacity and comparator Comparator<Integer> comparator = Integer::compareTo; BlockingQueue<Integer> queue3 = new PriorityBlockingQueue<>(5, comparator); // Constructor with initial collection of elements Collection<Integer> initialElements = java.util.Arrays.asList(1, 3, 2); BlockingQueue<Integer> queue4 = new PriorityBlockingQueue<>(initialElements)
DelayQueue : Une file d'attente différée où les éléments ne peuvent être récupérés qu'après l'expiration de leur délai.
DelayedElement.java
DelayQueueConstructors.java
123456789101112131415161718class DelayedElement implements Delayed { private final long expirationTime; // The time when the element will be available public DelayedElement(long delay, TimeUnit unit) { this.expirationTime = System.currentTimeMillis() + unit.toMillis(delay); } @Override public long getDelay(TimeUnit unit) { long delay = expirationTime - System.currentTimeMillis(); // Calculate the remaining delay return unit.convert(delay, TimeUnit.MILLISECONDS); // Convert the delay to the specified time unit } @Override public int compareTo(Delayed o) { return Long.compare(this.expirationTime, ((DelayedElement) o).expirationTime); } }
Ce code illustre l'utilisation de la classe DelayedElement, qui implémente l'interface Delayed, ainsi que la file d'attente différée DelayQueue en Java. La classe DelayedElement définit une méthode getDelay pour calculer le temps de délai restant et une méthode compareTo pour comparer les objets en fonction du temps d'expiration du délai.
La méthode main crée deux files d'attente : queue1, une file d'attente différée vide, et queue2, une file initialisée avec des éléments ayant respectivement un délai de 5 et 1 seconde.
Les éléments dans DelayQueueue deviennent disponibles pour la récupération après l'écoulement du temps de délai spécifié.
SynchronousQueueue : Une file d'attente sans capacité, où chaque opération d'insertion doit attendre l'opération d'extraction correspondante et vice versa.
Main.java
12345// Constructor without fair access BlockingQueue<String> queue1 = new SynchronousQueue<>(); // Constructor with fair access BlockingQueue<String> queue2 = new SynchronousQueue<>(true);
Principales méthodes de BlockingQueue :
Ajout d'éléments :
La méthode void put(E e) insère un élément dans la file d'attente, bloquant le thread si la file est pleine. Alternativement, la méthode boolean offer(E e, long timeout, TimeUnit unit) tente d'ajouter un élément à la file d'attente, en attendant le délai spécifié si la file est pleine.
Main.java
1234567891011121314151617public class BlockingQueueExample { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<>(2); try { queue.put("Element 1"); // Insert the first element, no blocking. queue.put("Element 2"); // Insert the second element, no blocking. // Try to add the third element with a 2-second timeout. // Since the queue is full, it will wait for 2 seconds. boolean success = queue.offer("Element 3", 2, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } } }
Cet exemple illustre l’insertion de deux éléments dans une BlockingQueue sans blocage, suivie d’une tentative d’ajout d’un troisième élément avec un délai d’attente de 2 secondes à l’aide de la méthode offer(), qui attendra si la file d’attente est pleine.
Récupération d’élément :
La méthode E take() récupère et retourne un élément de la file d’attente, en bloquant le thread si la file est vide. Alternativement, la méthode E poll(long timeout, TimeUnit unit) tente de récupérer un élément de la file d’attente, en attendant pendant la durée spécifiée si la file est vide.
Main.java
1234567891011121314151617181920212223public class BlockingQueueRetrievalExample { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<>(2); try { // Adding elements to the queue queue.put("Element 1"); queue.put("Element 2"); // Retrieve and remove the first element, no blocking since the queue is not empty String item1 = queue.take(); // Returns "Element 1" // Attempt to retrieve and remove the next element with a 2-second timeout String item2 = queue.poll(2, TimeUnit.SECONDS); // Returns "Element 2" // Attempt to retrieve an element when the queue is empty, this will block for 2 seconds String item3 = queue.poll(2, TimeUnit.SECONDS); // Returns `null` after timeout } catch (InterruptedException e) { e.printStackTrace(); } } }
Ce code ajoute deux éléments à une BlockingQueue, récupère et retire immédiatement le premier élément, tente de récupérer le prochain élément avec un délai d'attente de 2 secondes, puis essaie enfin de récupérer un élément depuis une file d'attente vide, ce qui aboutit à une valeur null après le délai d'attente.
Vérification et suppression des éléments :
La méthode boolean remove(Object o) supprime l'élément spécifié de la file d'attente s'il est présent. En revanche, la méthode boolean contains(Object o) vérifie si l'élément spécifié est présent dans la file d'attente sans le supprimer.
Main.java
1234567891011121314151617181920212223242526public class BlockingQueueCheckRemoveExample { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<>(2); try { // Adding elements to the queue queue.put("Element 1"); queue.put("Element 2"); // Check if "Element 1" is in the queue, should return `true` boolean containsElement1 = queue.contains("Element 1"); // true // Remove "Element 1" from the queue, should return `true` boolean removedElement1 = queue.remove("Element 1"); // true // Check if "Element 1" is still in the queue, should return `false` boolean containsElement1AfterRemoval = queue.contains("Element 1"); // false // Try to remove an element that is not in the queue, should return `false` boolean removedElement3 = queue.remove("Element 3"); // false } catch (InterruptedException e) { e.printStackTrace(); } } }
Ce code ajoute deux éléments à une BlockingQueue, vérifie la présence de "Element 1", le supprime, vérifie à nouveau pour confirmer sa suppression, puis tente de supprimer un élément inexistant.
Interrogation de l'état de la file d'attente :
La méthode int size() retourne le nombre d'éléments actuellement présents dans la file d'attente. Pour déterminer si la file d'attente est vide, il est possible d'utiliser la méthode boolean isEmpty(), qui vérifie si la file ne contient aucun élément. Pour les files d'attente à capacité fixe, la méthode int remainingCapacity() indique le nombre de places restantes disponibles dans la file.
Main.java
123456789101112131415161718192021222324252627282930public class BlockingQueueCapacityExample { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<>(3); try { // Adding elements to the queue queue.put("Element 1"); queue.put("Element 2"); // Get the number of elements in the queue int currentSize = queue.size(); // 2 // Check if the queue is empty boolean isQueueEmpty = queue.isEmpty(); // false // Get the remaining capacity in the queue int remainingSpace = queue.remainingCapacity(); // 1 // Add another element to fill the queue queue.put("Element 3"); // Check the size and remaining capacity after adding the third element currentSize = queue.size(); // 3 remainingSpace = queue.remainingCapacity(); // 0 } catch (InterruptedException e) { e.printStackTrace(); } } }
Ce code ajoute des éléments à une BlockingQueue, vérifie la taille actuelle, détermine si la file d'attente est vide, et identifie la capacité restante, puis met à jour ces valeurs après avoir complètement rempli la file d'attente.
Réalisation d'un exemple concret en code
😭 Limitations
Une limitation majeure est la performance : en raison des opérations de verrouillage impliquées, les performances peuvent être réduites par rapport aux collections non synchronisées. De plus, les ressources peuvent devenir un problème car les files d'attente volumineuses nécessitent davantage de mémoire et de temps CPU pour gérer les verrous et les processus de synchronisation.
💪 Avantages
Du côté positif, le système est sécurisé en multithreading, offrant une communication sûre entre les threads sans nécessiter de gestion manuelle de la synchronisation. Il simplifie également le code en évitant les constructions complexes de synchronisation et de blocage. En outre, la flexibilité des différentes implémentations de BlockingQueue permet de les adapter à divers scénarios d'utilisation.
1. Qu'est-ce qu'une BlockingQueue en Java ?
2. Quelles sont les principales méthodes de BlockingQueue qui bloquent un thread ?
3. À quoi sert BlockingQueue dans les applications multithreadées ?
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
What are the main differences between the BlockingQueue implementations?
Can you explain how the DelayQueue works in more detail?
How does the producer-consumer example work in code?
Awesome!
Completion rate improved to 3.33
BlockingQueue et Ses Implémentations
Glissez pour afficher le menu
Implémentations de base de BlockingQueue
Nous n'aborderons pas chaque implémentation en détail, car cela prendrait beaucoup de temps et il est peu probable que vous ayez besoin de toutes. Nous allons discuter des concepts généraux et des constructeurs disponibles.
Exemple concret
Imaginez une usine où un thread, le producteur, fabrique des pièces, et un autre thread, le consommateur, les traite. Le producteur place les pièces dans une file d'attente, tandis que le consommateur les récupère et les traite à partir de cette file. Si la file d'attente est vide, le consommateur attend que le producteur en ajoute de nouvelles. À l'inverse, si la file est pleine, le producteur attend que le consommateur libère de la place.
Un peu plus bas, nous allons implémenter cette tâche en code.
Différences avec d'autres types de collections
La BlockingQueue offre une synchronisation automatisée, gérant l'accès des threads à la file sans nécessiter de synchronisation manuelle. Elle prend également en charge des opérations bloquantes pour l'ajout et la récupération d'éléments, une fonctionnalité absente des autres collections comme ArrayList ou LinkedList.
Implémentations de BlockingQueue
ArrayBlockingQueue : Une file d'attente à taille limitée qui utilise un tableau pour stocker les éléments.
Main.java
123456789// Constructor with fixed capacity BlockingQueue<String> queue1 = new ArrayBlockingQueue<>(5); // Constructor with fixed capacity and fair access BlockingQueue<String> queue2 = new ArrayBlockingQueue<>(5, true); // Constructor with fixed capacity and initial collection of elements Collection<String> initialElements = java.util.Arrays.asList("One", "Two", "Three"); BlockingQueue<String> queue3 = new ArrayBlockingQueue<>(5, false, initialElements);
Le paramètre true active une politique d'accès équitable en fournissant un ordre FIFO pour l'accès des threads.
LinkedBlockingQueueue : Une file d'attente basée sur des nœuds chaînés pouvant être restreinte ou non restreinte.
Main.java
123456789// Constructor without capacity bounds BlockingQueue<String> queue1 = new LinkedBlockingQueue<>(); // Constructor with fixed capacity BlockingQueue<String> queue2 = new LinkedBlockingQueue<>(5); // Constructor with initial collection of elements Collection<String> initialElements = java.util.Arrays.asList("One", "Two", "Three"); BlockingQueue<String> queue3 = new LinkedBlockingQueue<>(initialElements);
PriorityBlockingQueue : Une file d'attente prioritaire non bornée où les éléments sont extraits selon leur ordre naturel ou tel que défini par un comparateur.
Main.java
12345678910111213// Constructor without initial capacity (default is 11) BlockingQueue<Integer> queue1 = new PriorityBlockingQueue<>(); // Constructor with initial capacity BlockingQueue<Integer> queue2 = new PriorityBlockingQueue<>(5); // Constructor with initial capacity and comparator Comparator<Integer> comparator = Integer::compareTo; BlockingQueue<Integer> queue3 = new PriorityBlockingQueue<>(5, comparator); // Constructor with initial collection of elements Collection<Integer> initialElements = java.util.Arrays.asList(1, 3, 2); BlockingQueue<Integer> queue4 = new PriorityBlockingQueue<>(initialElements)
DelayQueue : Une file d'attente différée où les éléments ne peuvent être récupérés qu'après l'expiration de leur délai.
DelayedElement.java
DelayQueueConstructors.java
123456789101112131415161718class DelayedElement implements Delayed { private final long expirationTime; // The time when the element will be available public DelayedElement(long delay, TimeUnit unit) { this.expirationTime = System.currentTimeMillis() + unit.toMillis(delay); } @Override public long getDelay(TimeUnit unit) { long delay = expirationTime - System.currentTimeMillis(); // Calculate the remaining delay return unit.convert(delay, TimeUnit.MILLISECONDS); // Convert the delay to the specified time unit } @Override public int compareTo(Delayed o) { return Long.compare(this.expirationTime, ((DelayedElement) o).expirationTime); } }
Ce code illustre l'utilisation de la classe DelayedElement, qui implémente l'interface Delayed, ainsi que la file d'attente différée DelayQueue en Java. La classe DelayedElement définit une méthode getDelay pour calculer le temps de délai restant et une méthode compareTo pour comparer les objets en fonction du temps d'expiration du délai.
La méthode main crée deux files d'attente : queue1, une file d'attente différée vide, et queue2, une file initialisée avec des éléments ayant respectivement un délai de 5 et 1 seconde.
Les éléments dans DelayQueueue deviennent disponibles pour la récupération après l'écoulement du temps de délai spécifié.
SynchronousQueueue : Une file d'attente sans capacité, où chaque opération d'insertion doit attendre l'opération d'extraction correspondante et vice versa.
Main.java
12345// Constructor without fair access BlockingQueue<String> queue1 = new SynchronousQueue<>(); // Constructor with fair access BlockingQueue<String> queue2 = new SynchronousQueue<>(true);
Principales méthodes de BlockingQueue :
Ajout d'éléments :
La méthode void put(E e) insère un élément dans la file d'attente, bloquant le thread si la file est pleine. Alternativement, la méthode boolean offer(E e, long timeout, TimeUnit unit) tente d'ajouter un élément à la file d'attente, en attendant le délai spécifié si la file est pleine.
Main.java
1234567891011121314151617public class BlockingQueueExample { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<>(2); try { queue.put("Element 1"); // Insert the first element, no blocking. queue.put("Element 2"); // Insert the second element, no blocking. // Try to add the third element with a 2-second timeout. // Since the queue is full, it will wait for 2 seconds. boolean success = queue.offer("Element 3", 2, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } } }
Cet exemple illustre l’insertion de deux éléments dans une BlockingQueue sans blocage, suivie d’une tentative d’ajout d’un troisième élément avec un délai d’attente de 2 secondes à l’aide de la méthode offer(), qui attendra si la file d’attente est pleine.
Récupération d’élément :
La méthode E take() récupère et retourne un élément de la file d’attente, en bloquant le thread si la file est vide. Alternativement, la méthode E poll(long timeout, TimeUnit unit) tente de récupérer un élément de la file d’attente, en attendant pendant la durée spécifiée si la file est vide.
Main.java
1234567891011121314151617181920212223public class BlockingQueueRetrievalExample { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<>(2); try { // Adding elements to the queue queue.put("Element 1"); queue.put("Element 2"); // Retrieve and remove the first element, no blocking since the queue is not empty String item1 = queue.take(); // Returns "Element 1" // Attempt to retrieve and remove the next element with a 2-second timeout String item2 = queue.poll(2, TimeUnit.SECONDS); // Returns "Element 2" // Attempt to retrieve an element when the queue is empty, this will block for 2 seconds String item3 = queue.poll(2, TimeUnit.SECONDS); // Returns `null` after timeout } catch (InterruptedException e) { e.printStackTrace(); } } }
Ce code ajoute deux éléments à une BlockingQueue, récupère et retire immédiatement le premier élément, tente de récupérer le prochain élément avec un délai d'attente de 2 secondes, puis essaie enfin de récupérer un élément depuis une file d'attente vide, ce qui aboutit à une valeur null après le délai d'attente.
Vérification et suppression des éléments :
La méthode boolean remove(Object o) supprime l'élément spécifié de la file d'attente s'il est présent. En revanche, la méthode boolean contains(Object o) vérifie si l'élément spécifié est présent dans la file d'attente sans le supprimer.
Main.java
1234567891011121314151617181920212223242526public class BlockingQueueCheckRemoveExample { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<>(2); try { // Adding elements to the queue queue.put("Element 1"); queue.put("Element 2"); // Check if "Element 1" is in the queue, should return `true` boolean containsElement1 = queue.contains("Element 1"); // true // Remove "Element 1" from the queue, should return `true` boolean removedElement1 = queue.remove("Element 1"); // true // Check if "Element 1" is still in the queue, should return `false` boolean containsElement1AfterRemoval = queue.contains("Element 1"); // false // Try to remove an element that is not in the queue, should return `false` boolean removedElement3 = queue.remove("Element 3"); // false } catch (InterruptedException e) { e.printStackTrace(); } } }
Ce code ajoute deux éléments à une BlockingQueue, vérifie la présence de "Element 1", le supprime, vérifie à nouveau pour confirmer sa suppression, puis tente de supprimer un élément inexistant.
Interrogation de l'état de la file d'attente :
La méthode int size() retourne le nombre d'éléments actuellement présents dans la file d'attente. Pour déterminer si la file d'attente est vide, il est possible d'utiliser la méthode boolean isEmpty(), qui vérifie si la file ne contient aucun élément. Pour les files d'attente à capacité fixe, la méthode int remainingCapacity() indique le nombre de places restantes disponibles dans la file.
Main.java
123456789101112131415161718192021222324252627282930public class BlockingQueueCapacityExample { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<>(3); try { // Adding elements to the queue queue.put("Element 1"); queue.put("Element 2"); // Get the number of elements in the queue int currentSize = queue.size(); // 2 // Check if the queue is empty boolean isQueueEmpty = queue.isEmpty(); // false // Get the remaining capacity in the queue int remainingSpace = queue.remainingCapacity(); // 1 // Add another element to fill the queue queue.put("Element 3"); // Check the size and remaining capacity after adding the third element currentSize = queue.size(); // 3 remainingSpace = queue.remainingCapacity(); // 0 } catch (InterruptedException e) { e.printStackTrace(); } } }
Ce code ajoute des éléments à une BlockingQueue, vérifie la taille actuelle, détermine si la file d'attente est vide, et identifie la capacité restante, puis met à jour ces valeurs après avoir complètement rempli la file d'attente.
Réalisation d'un exemple concret en code
😭 Limitations
Une limitation majeure est la performance : en raison des opérations de verrouillage impliquées, les performances peuvent être réduites par rapport aux collections non synchronisées. De plus, les ressources peuvent devenir un problème car les files d'attente volumineuses nécessitent davantage de mémoire et de temps CPU pour gérer les verrous et les processus de synchronisation.
💪 Avantages
Du côté positif, le système est sécurisé en multithreading, offrant une communication sûre entre les threads sans nécessiter de gestion manuelle de la synchronisation. Il simplifie également le code en évitant les constructions complexes de synchronisation et de blocage. En outre, la flexibilité des différentes implémentations de BlockingQueue permet de les adapter à divers scénarios d'utilisation.
1. Qu'est-ce qu'une BlockingQueue en Java ?
2. Quelles sont les principales méthodes de BlockingQueue qui bloquent un thread ?
3. À quoi sert BlockingQueue dans les applications multithreadées ?
Merci pour vos commentaires !