Класс Java PriorityBlockingQueue

Класс Java PriorityBlockingQueue — это реализация структуры данных очереди с параллельным блокированием, в которой объекты обрабатываются на основе их приоритета. Часть имени «blocking» добавлена для того, чтобы указать, что поток будет блокироваться в ожидании, пока в очереди не появится доступный элемент.

В очереди приоритетной блокировки добавленные объекты упорядочиваются в соответствии с их приоритетом. По умолчанию приоритет определяется естественным порядком объектов. Приоритет по умолчанию может быть переопределен компаратором, предоставленным во время построения очереди.

Очередь приоритетной блокировки
Очередь приоритетной блокировки

1. Функции PriorityBlockingQueue

Давайте отметим несколько важных моментов относительно PriorityBlockingQueue.

  • PriorityBlockingQueue — это неограниченная очередь, которая растет динамически. Начальная емкость по умолчанию — '11', ее можно переопределить с помощью параметра initialCapacity в соответствующем конструкторе.
  • Обеспечивает блокировку операций по извлечению.
  • Не допускается наличие объектов NULL.
  • Объекты, добавляемые в PriorityBlockingQueue, ДОЛЖНЫ быть сопоставимыми, в противном случае будет выдана исключительная ситуация ClassCastException.
  • Объекты приоритетной очереди по умолчанию упорядочены в естественном порядке.
  • Компаратор можно использовать для индивидуального упорядочивания объектов в очереди.
  • Голова приоритетной очереди — это наименьший элемент на основе естественного порядка или порядка на основе компаратора. Когда мы опрашиваем очередь, она возвращает головной объект из очереди.
  • Если имеется несколько объектов с одинаковым приоритетом, он может опросить любой из них случайным образом.
  • PriorityBlockingQueue является потокобезопасным.
  • Iterator, предоставленный в методе iterator(), не гарантирует обход элементов PriorityBlockingQueue в каком-либо определенном порядке. Если вам нужен упорядоченный обход, рассмотрите возможность использования Arrays.sort(pbq.toArray()).
  • Функция drainTo() может использоваться для удаления некоторых или всех элементов в порядке приоритета и помещения их в другую коллекцию.

2. Пример очереди приоритетной блокировки Java

Давайте посмотрим, как порядок объектов влияет на операции добавления и удаления в PriorityBlockingQueue. В приведенных примерах объекты имеют тип Employee. Класс Employee реализует интерфейс Comparable, который делает объекты сопоставимыми по полю 'id' сотрудника по умолчанию.

public class Employee implements Comparable<Employee> {private Long id;private String name;private LocalDate dob;public Employee(Long id, String name, LocalDate dob) {super();this.id = id;this.name = name;this.dob = dob;}@Overridepublic int compareTo(Employee emp) {return this.getId().compareTo(emp.getId());}//Getters and setters@Overridepublic String toString() {return "Employee [id=" + id + ", name=" + name + ", dob=" + dob + "]";}}

2.1 Естественный порядок

Пример Java PriorityBlockingQueue для добавления и опроса элементов, которые сравниваются на основе их естественного порядка.

PriorityBlockingQueue<Employee> PriorityBlockingQueue = new PriorityBlockingQueue<>();PriorityBlockingQueue.add(new Employee(1l, "AAA", LocalDate.now()));PriorityBlockingQueue.add(new Employee(4l, "CCC", LocalDate.now()));PriorityBlockingQueue.add(new Employee(5l, "BBB", LocalDate.now()));PriorityBlockingQueue.add(new Employee(2l, "FFF", LocalDate.now()));PriorityBlockingQueue.add(new Employee(3l, "DDD", LocalDate.now()));PriorityBlockingQueue.add(new Employee(6l, "EEE", LocalDate.now()));while(true){Employee e = PriorityBlockingQueue.poll();System.out.println(e);if(e == null) break;}

Вывод программы.

Employee [id=1, name=AAA, dob=2018-10-31]Employee [id=2, name=FFF, dob=2018-10-31]Employee [id=5, name=BBB, dob=2018-10-31]Employee [id=4, name=CCC, dob=2018-10-31]Employee [id=3, name=DDD, dob=2018-10-31]Employee [id=6, name=EEE, dob=2018-10-31]

2.2. Пример компаратора PriorityBlockingQueue

Давайте переопределим пользовательский порядок, используя синтаксис компаратора на основе лямбда-выражений Java 8, и проверим результат. Мы используем конструктор PriorityBlockingQueue(int initialCapacity, Comparator comparator).

//Comparator for name fieldComparator<Employee> nameSorter = Comparator.comparing(Employee::getName);PriorityBlockingQueue<Employee> PriorityBlockingQueue = new PriorityBlockingQueue<>( 11, nameSorter );PriorityBlockingQueue.add(new Employee(1l, "AAA", LocalDate.now()));PriorityBlockingQueue.add(new Employee(4l, "CCC", LocalDate.now()));PriorityBlockingQueue.add(new Employee(5l, "BBB", LocalDate.now()));PriorityBlockingQueue.add(new Employee(2l, "FFF", LocalDate.now()));PriorityBlockingQueue.add(new Employee(3l, "DDD", LocalDate.now()));PriorityBlockingQueue.add(new Employee(6l, "EEE", LocalDate.now()));while(true){Employee e = PriorityBlockingQueue.poll();System.out.println(e);if(e == null) break;}

Вывод программы.

Employee [id=1, name=AAA, dob=2018-10-31]Employee [id=5, name=BBB, dob=2018-10-31]Employee [id=4, name=CCC, dob=2018-10-31]Employee [id=3, name=DDD, dob=2018-10-31]Employee [id=6, name=EEE, dob=2018-10-31]Employee [id=2, name=FFF, dob=2018-10-31]

2.3. Пример PriorityBlockingQueue drainTo()

Пример Java, в котором используется метод drianTo() для извлечения нескольких элементов из очереди в одном операторе.

PriorityBlockingQueue<Integer> priorityBlockingQueue = new PriorityBlockingQueue<>();priorityBlockingQueue.add(1);priorityBlockingQueue.add(3);priorityBlockingQueue.add(2);priorityBlockingQueue.add(6);priorityBlockingQueue.add(4);priorityBlockingQueue.add(5);ArrayList<Integer> list = new ArrayList<>();//Drain first 3 elementspriorityBlockingQueue.drainTo(list, 3);System.out.println(list);//Drain all elementspriorityBlockingQueue.drainTo(list);System.out.println(list);

Вывод программы.

[1, 2, 3][1, 2, 3, 4, 5, 6]

2.4. Пример извлечения блокировки PriorityBlockingQueue

Пример Java для извлечения элементов из PriorityBlockingQueue с помощью блокирующего извлечения. Поток будет ждать, пока в очереди не появится элемент.

В данном примере поток ждет в очереди в бесконечном цикле, используя метод take(). Он ждет 1 секунду перед повторной проверкой. Как только мы добавляем элементы в очередь, он опрашивает элемент и выводит на консоль.

import java.util.concurrent.PriorityBlockingQueue;import java.util.concurrent.TimeUnit;public class PriorityQueueExample{public static void main(String[] args) throws InterruptedException{PriorityBlockingQueue<Integer> priorityBlockingQueue = new PriorityBlockingQueue<>();new Thread(() ->{System.out.println("Waiting to poll ...");try{while(true){Integer poll = priorityBlockingQueue.take();System.out.println("Polled : " + poll);Thread.sleep(TimeUnit.SECONDS.toMillis(1));}} catch(InterruptedException e) {e.printStackTrace();}}).start();Thread.sleep(TimeUnit.SECONDS.toMillis(2));priorityBlockingQueue.add(1);Thread.sleep(TimeUnit.SECONDS.toMillis(2));priorityBlockingQueue.add(2);Thread.sleep(TimeUnit.SECONDS.toMillis(2));priorityBlockingQueue.add(3);}}

Вывод программы.

Waiting to poll ...Polled : 1Polled : 2Polled : 3

3. Конструкторы очереди приоритетной блокировки Java

Класс PriorityBlockingQueue предоставляет 4 различных способа построения приоритетной очереди в Java.

  • PriorityBlockingQueue() : создает пустую очередь с начальной емкостью по умолчанию(11), которая упорядочивает свои элементы в соответствии с их естественным порядком.
  • PriorityBlockingQueue(Collection c) : создает пустую очередь, содержащую элементы указанной коллекции.
  • PriorityBlockingQueue(int initialCapacity): создает пустую очередь с указанной начальной емкостью, которая упорядочивает свои элементы в соответствии с их естественным порядком.
  • PriorityBlockingQueue(int initialCapacity, Comparator comparator) : создает пустую очередь с указанной начальной емкостью, которая упорядочивает свои элементы в соответствии с указанным компаратором.

4. Методы приоритетной блокировки очереди Java

Класс PriorityBlockingQueue имеет нижеприведенные важные методы, которые вам следует знать.

  • boolean add(object) : вставляет указанный элемент в эту приоритетную очередь.
  • boolean offer(object) : вставляет указанный элемент в эту приоритетную очередь.
  • boolean remove(object) : удаляет один экземпляр указанного элемента из этой очереди, если он присутствует.
  • Object poll() : извлекает и удаляет заголовок этой очереди, ожидая в течение указанного времени ожидания, если это необходимо, чтобы элемент стал доступен.
  • Object poll(timeout, timeUnit): извлекает и удаляет заголовок этой очереди, ожидая в течение указанного времени ожидания, если это необходимо для того, чтобы элемент стал доступен.
  • Object take() : извлекает и удаляет заголовок этой очереди, при необходимости ожидая, пока элемент станет доступным.
  • void put(Object o) : вставляет указанный элемент в эту приоритетную очередь.
  • void clear() : удаляет все элементы из этой приоритетной очереди.
  • Компаратор comparator() : возвращает компаратор, используемый для упорядочивания элементов в этой очереди, или null, если эта очередь отсортирована в соответствии с естественным порядком ее элементов.
  • boolean contains(Object o) : возвращает true, если эта очередь содержит указанный элемент.
  • Итератор iterator() : возвращает итератор по элементам в этой очереди.
  • int size() : возвращает количество элементов в этой очереди.
  • int drainTo(Collection c) : удаляет все доступные элементы из этой очереди и добавляет их в указанную коллекцию.
  • int drainTo(Collection c, int maxElements) : удаляет не более указанного количества доступных элементов из этой очереди и добавляет их в указанную коллекцию.
  • int remainCapacity() : всегда возвращает Integer.MAX_VALUE, поскольку PriorityBlockingQueue не имеет ограничений по емкости.
  • Object[] toArray() : возвращает массив, содержащий все элементы в этой очереди.

Обратите внимание на разницу между методами take() и poll(). Poll() извлекает и удаляет заголовок этой очереди или возвращает null, если эта очередь пуста. Это не блокирующая операция.

Take() извлекает и удаляет главу этой очереди, ожидая при необходимости, пока элемент не станет доступным. Это блокирующая операция.

5. Заключение

В этом руководстве по Java PriorityBlockingQueue мы научились использовать класс PriorityBlockingQueue, который может хранить элементы либо в естественном порядке по умолчанию, либо в пользовательском порядке, указанном компаратором.

Мы также изучили несколько важных методов и конструкторов класса PriorityBlockingQueue.

Пишите мне свои вопросы в разделе комментариев.

Ссылки:

Документация Java по классу PriorityBlockingQueue

Прокрутить вверх