Guide de PriorityBlockingQueue en Java

Guide de PriorityBlockingQueue en Java

1. introduction

Dans cet article, nous allons nous concentrer sur la classePriorityBlockingQueue et passer en revue quelques exemples pratiques.

En partant de l'hypothèse que nous savons déjà ce qu'est unQueue, nous allons d'abord démontrerhow elements in the PriorityBlockingQueue are ordered by priority.

Ensuite, nous montrerons comment ce type de file d’attente peut être utilisé pour bloquer un thread.

Enfin, nous montrerons en quoi l'utilisation conjointe de ces deux fonctionnalités peut s'avérer utile lors du traitement de données sur plusieurs threads.

2. Priorité des éléments

Contrairement à une file d'attente standard, vous ne pouvez pas simplement ajouter n'importe quel type d'élément à unPriorityBlockingQueue.. Il existe deux options:

  1. Ajout d'éléments qui implémententComparable

  2. Ajout d'éléments qui n'implémentent pasComparable, à condition que vous fournissiez également unComparator

En utilisant les implémentationsComparator ouComparable pour comparer des éléments, lesPriorityBlockingQueue seront toujours triés.

Le but est de mettre en œuvre une logique de comparaison de manière à ce quethe highest priority element is always ordered first. Ensuite, lorsque nous retirons un élément de notre file d’attente, ce sera toujours celui qui a la priorité la plus élevée.

Pour commencer, utilisons notre file d’attente dans un seul thread, au lieu de l’utiliser sur plusieurs. De cette manière, il est facile de prouver comment les éléments sont ordonnés dans un test unitaire:

PriorityBlockingQueue queue = new PriorityBlockingQueue<>();
ArrayList polledElements = new ArrayList<>();

queue.add(1);
queue.add(5);
queue.add(2);
queue.add(3);
queue.add(4);

queue.drainTo(polledElements);

assertThat(polledElements).containsExactly(1, 2, 3, 4, 5);

Comme nous pouvons le constater, même si les éléments de la file d’attente ont été ajoutés dans un ordre aléatoire, ils seront ordonnés lorsque nous commencerons à les interroger. C'est parce que la classeInteger implémenteComparable, qui, à son tour, sera utilisé pour s'assurer que nous les sortons de la file d'attente dans l'ordre croissant.

Il convient également de noter quewhen two elements are compared and are the same, there’s no guarantee of how they will be ordered.

3. Utiliser lesQueue pour bloquer

Si nous avions affaire à une file d'attente standard, nous appellerionspoll() pour récupérer des éléments. Cependant, si la file d'attente était vide, un appel àpoll() renverraitnull.

LePriorityBlockingQueue implémente l'interfaceBlockingQueue, ce qui nous donne quelques méthodes supplémentaires qui nous permettent deblock when removing from an empty queue. Essayons d'utiliser la méthodetake(), qui devrait faire exactement cela:

PriorityBlockingQueue queue = new PriorityBlockingQueue<>();

new Thread(() -> {
  System.out.println("Polling...");

  try {
      Integer poll = queue.take();
      System.out.println("Polled: " + poll);
  } catch (InterruptedException e) {
      e.printStackTrace();
  }
}).start();

Thread.sleep(TimeUnit.SECONDS.toMillis(5));
System.out.println("Adding to queue");
queue.add(1);

Bien que l'utilisation desleep() soit une manière légèrement fragile de démontrer les choses, lorsque nous exécuterons ce code, nous verrons:

Polling...
Adding to queue
Polled: 1

Cela prouve quetake() est bloqué jusqu'à ce qu'un élément soit ajouté:

  1. Le fil imprimera "Interrogation" pour prouver qu'il a commencé

  2. Le test se mettra ensuite en pause pendant environ cinq secondes, pour prouver que le thread doit avoir appelétake() à ce stade

  3. Nous ajoutons à la file d'attente, et devrions voir plus ou moins instantanément «Polled: 1» pour prouver quetake() a renvoyé un élément dès qu'il est disponible

Il convient également de mentionner que l'interfaceBlockingQueue nous fournit également des moyens de bloquer lors de l'ajout à des files d'attente pleines.

Cependant, unPriorityBlockingQueue est illimité. Cela signifie qu'il ne sera jamais complet et qu'il sera donc toujours possible d'ajouter de nouveaux éléments.

4. Utilisation conjointe du blocage et de la priorisation

Maintenant que nous avons expliqué les deux concepts clés d'unPriorityBlockingQueue,, utilisons les deux ensemble. Nous pouvons simplement développer notre exemple précédent, mais cette fois-ci, nous ajouterons d'autres éléments à la file d'attente:

Thread thread = new Thread(() -> {
    System.out.println("Polling...");
    while (true) {
        try {
            Integer poll = queue.take();
            System.out.println("Polled: " + poll);
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});

thread.start();

Thread.sleep(TimeUnit.SECONDS.toMillis(5));
System.out.println("Adding to queue");

queue.addAll(newArrayList(1, 5, 6, 1, 2, 6, 7));
Thread.sleep(TimeUnit.SECONDS.toMillis(1));

Encore une fois, bien que ce soit un peu fragile à cause de l'utilisation desleep(),, cela nous montre toujours un cas d'utilisation valide. Nous avons maintenant une file d'attente qui se bloque en attendant l'ajout d'éléments. Nous ajoutons ensuite de nombreux éléments à la fois, puis nous montrons qu'ils seront traités par ordre de priorité. La sortie ressemblera à ceci:

Polling...
Adding to queue
Polled: 1
Polled: 1
Polled: 2
Polled: 5
Polled: 6
Polled: 6
Polled: 7

5. Conclusion

Dans ce guide, nous avons montré comment nous pouvons utiliser unPriorityBlockingQueue pour bloquer un fil jusqu'à ce que certains éléments y soient ajoutés, et également que nous sommes en mesure de traiter ces éléments en fonction de leur priorité.

L'implémentation de ces exemples peut être trouvéeover on GitHub. Ceci est un projet basé sur Maven, il devrait donc être facile à exécuter tel quel.