Filas sem Bloqueio

Implementações de fila seguras para threads e, o mais importante, sem bloqueio em nós vinculados.

ConcurrentLinkedQueue<E> - usa um algoritmo sem espera adaptado para trabalhar com o coletor de lixo. Este algoritmo é bastante eficiente e muito rápido, pois é construído sobre o CAS. O método size() pode ser executado por um longo período de tempo, portanto, é melhor não puxá-lo o tempo todo.

ConcurrentLinkedDeque<E> - Deque significa Fila dupla. Isso significa que os dados podem ser adicionados e extraídos de ambos os lados. Assim, a classe suporta ambos os modos de operação: FIFO (First In First Out) e LIFO (Last In First Out).

Na prática, ConcurrentLinkedDeque deve ser utilizado caso o LIFO seja absolutamente necessário, pois devido à bidirecionalidade dos nós, esta classe perde metade do desempenho em relação a ConcurrentLinkedQueue .

import java.util.concurrent.ConcurrentLinkedQueue;

public class  ConcurrentLinkedQueueExample {
   public static void main(String[] args) {
       ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();

       Thread producer = new Thread(new Producer(queue));
       Thread consumer = new Thread(new Consumer(queue));

       producer.start();
       consumer.start();
   }
}

class Producer implements Runnable {

   ConcurrentLinkedQueue<String> queue;
   Producer(ConcurrentLinkedQueue<String> queue){
       this.queue = queue;
   }
   public void run() {
       System.out.println("Class for adding items to the queue");
       try {
           for (int i = 1; i < 5; i++) {
               queue.add("Item #" + i);
               System.out.println("Added: Item #" + i);
               Thread.sleep(300);
           }
       } catch (InterruptedException ex) {
           ex.printStackTrace();
           Thread.currentThread().interrupt();
       }
   }
}

class Consumer implements Runnable {

   ConcurrentLinkedQueue<String> queue;
   Consumer(ConcurrentLinkedQueue<String> queue){
       this.queue = queue;
   }

   public void run() {
       String str;
       System.out.println("Class for getting items from the queue");
       for (int x = 0; x < 5; x++) {
           while ((str = queue.poll()) != null) {
               System.out.println("Pulled out: " + str);
           }
           try {
               Thread.sleep(600);
           } catch (InterruptedException ex) {
               ex.printStackTrace();
               Thread.currentThread().interrupt();
           }
       }
   }
}

Bloqueio de Filas

Interface BlockingQueue<E> - se houver muitos dados, ConcurrentLinkedQueue não será suficiente.

Quando os threads falham em fazer seu trabalho, você pode facilmente obter um OutOfMemmoryException . E para que esses casos não ocorram, temos um BlockingQueue para trabalhar com a presença de diferentes métodos de preenchimento e trabalho com a fila e bloqueios condicionais.

BlockingQueue não reconhece elementos nulos e lança um NullPointerException ao tentar adicionar ou obter tal elemento. O método poll retorna um elemento nulo se nenhum elemento foi colocado na fila dentro do tempo limite.

Implementações BlockingQueue<E>

Vamos dar uma olhada em cada uma de nossas implementações BlockingQueue :

ArrayBlockingQueue<E> é uma classe de fila de bloqueio criada no buffer de anel clássico. Aqui temos a oportunidade de gerir a “honestidade” das fechaduras. Se justo=falso (o padrão), a ordem do encadeamento não é garantida.

DelayQueue<E extends Delayed> é uma classe que permite puxar elementos da fila somente após um certo atraso, definido em cada elemento através do método getDelay da interface Delayed .

LinkedBlockingQueue<E> é uma fila de bloqueio em nós vinculados, implementada no algoritmo “two lock queue”: o primeiro bloqueio é para adicionar, o segundo é para puxar um elemento da fila. Devido aos bloqueios, em comparação com ArrayBlockingQueue , esta classe tem alto desempenho, mas requer mais memória. O tamanho da fila é definido por meio do construtor e é igual a Integer.MAX_VALUE por padrão.

PriorityBlockingQueue<E> é um wrapper multiencadeado sobre PriorityQueue . O Comparator é responsável pela lógica pela qual o elemento será adicionado. O menor elemento sai primeiro.

SynchronousQueue<E> - a fila funciona de acordo com o princípio FIFO (first-in-first-out). Cada operação de inserção bloqueia o thread “Produtor” até que o segmento “Consumidor” puxe o elemento da fila e vice-versa, o “Consumidor” aguardará até que o “Produtor” insira o elemento.

BlockingDeque<E> é uma interface que descreve métodos adicionais para uma fila de bloqueio bidirecional. Os dados podem ser inseridos e retirados de ambos os lados da fila.

LinkedBlockingDeque<E> é uma fila de bloqueio bidirecional em nós vinculados, implementada como uma lista bidirecional simples com um bloqueio. O tamanho da fila é definido por meio do construtor e é igual a Integer.MAX_VALUE por padrão.

TransferQueue<E> - a interface é interessante porque, quando um elemento é adicionado à fila, é possível bloquear a inserção do encadeamento do produtor até que outro encadeamento do consumidor extraia o elemento da fila. Você também pode adicionar uma verificação para um tempo limite específico ou definir uma verificação para Consumer s pendentes . Como resultado, obtemos um mecanismo de transferência de dados com suporte para mensagens assíncronas e síncronas.

LinkedTransferQueue<E> é uma implementação de TransferQueue baseada no algoritmo Dual Queues with Slack. Faz uso intenso de CAS (veja acima) e thread parking quando ocioso.