Guia para PriorityBlockingQueue em Java
1. Introdução
Neste artigo, vamos nos concentrar na aulaPriorityBlockingQueue e examinar alguns exemplos práticos.
Partindo do pressuposto de que já sabemos o que é aQueue, primeiro demonstraremoshow elements in the PriorityBlockingQueue are ordered by priority.
A seguir, demonstraremos como esse tipo de fila pode ser usado para bloquear um encadeamento.
Por fim, mostraremos como o uso desses dois recursos juntos pode ser útil ao processar dados em vários segmentos.
2. Prioridade dos Elementos
Ao contrário de uma fila padrão, você não pode simplesmente adicionar qualquer tipo de elemento a umPriorityBlockingQueue.. Existem duas opções:
-
Adicionando elementos que implementamComparable
-
Adicionando elementos que não implementamComparable, com a condição de que você forneça umComparator também
Usando as implementaçõesComparator ouComparable para comparar elementos, oPriorityBlockingQueue sempre será classificado.
O objetivo é implementar a lógica de comparação de forma quethe highest priority element is always ordered first. Então, quando removermos um elemento da nossa fila, ele sempre será aquele com a maior prioridade.
Para começar, vamos usar nossa fila em um único encadeamento, em vez de usá-lo em vários. Ao fazer isso, fica fácil provar como os elementos são ordenados em um teste de unidade:
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);
Como podemos ver, apesar de adicionar os elementos à fila em uma ordem aleatória, eles serão ordenados quando começarmos a pesquisá-los. Isso ocorre porque a classeInteger implementaComparable, que, por sua vez, será usada para garantir que os retiremos da fila em ordem crescente.
Também é importante notar quewhen two elements are compared and are the same, there’s no guarantee of how they will be ordered.
3. Usando oQueue para bloquear
Se estivéssemos lidando com uma fila padrão, chamaríamospoll() para recuperar os elementos. No entanto, se a fila estava vazia, uma chamada parapoll() retornarianull.
OPriorityBlockingQueue implementa a interfaceBlockingQueue, o que nos dá alguns métodos extras que nos permitemblock when removing from an empty queue. Vamos tentar usar o métodotake(), que deve fazer exatamente isso:
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);
Embora usarsleep() seja uma forma um pouco frágil de demonstrar as coisas, quando executarmos este código, veremos:
Polling...
Adding to queue
Polled: 1
Isso prova quetake() bloqueou até que um item fosse adicionado:
-
O tópico irá imprimir “Polling” para provar que já começou
-
O teste irá pausar por cerca de cinco segundos, para provar que o thread deve ter chamadotake() neste ponto
-
Nós adicionamos à fila, e devemos ver mais ou menos instantaneamente "Polled: 1" para provar quetake() retornou um elemento assim que ele se tornou disponível
Também vale a pena mencionar que a interfaceBlockingQueue também nos fornece maneiras de bloquear ao adicionar a filas cheias.
No entanto, aPriorityBlockingQueue é ilimitado. Isso significa que nunca estará cheio, portanto, sempre será possível adicionar novos elementos.
4. Usando Bloqueio e Priorização Juntos
Agora que explicamos os dois conceitos-chave de aPriorityBlockingQueue,, vamos usar os dois juntos. Podemos simplesmente expandir nosso exemplo anterior, mas desta vez adicionar mais elementos à fila:
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));
Novamente, embora isso seja um pouco frágil por causa do uso desleep(),, ainda nos mostra um caso de uso válido. Agora temos uma fila que bloqueia, aguardando a inclusão de elementos. Estamos então adicionando muitos elementos de uma vez e mostrando que eles serão tratados em ordem de prioridade. A saída será assim:
Polling...
Adding to queue
Polled: 1
Polled: 1
Polled: 2
Polled: 5
Polled: 6
Polled: 6
Polled: 7
5. Conclusão
Neste guia, demonstramos como podemos usar umPriorityBlockingQueue para bloquear um thread até que alguns itens tenham sido adicionados a ele, e também que somos capazes de processar esses itens com base em sua prioridade.
A implementação desses exemplos pode ser encontradaover on GitHub. Este é um projeto baseado em Maven, portanto deve ser fácil de executar como está.