Code non bloccanti

Implementazioni della coda thread-safe e, soprattutto, non bloccanti sui nodi collegati.

ConcurrentLinkedQueue<E> : utilizza un algoritmo senza attesa adattato per funzionare con il Garbage Collector. Questo algoritmo è abbastanza efficiente e molto veloce, poiché è basato su CAS. Il metodo size() può essere eseguito per molto tempo, quindi è meglio non estrarlo continuamente.

ConcurrentLinkedDeque<E> - Deque sta per Double ended queue. Ciò significa che i dati possono essere aggiunti ed estratti da entrambi i lati. Di conseguenza, la classe supporta entrambe le modalità operative: FIFO (First In First Out) e LIFO (Last In First Out).

In pratica, ConcurrentLinkedDeque dovrebbe essere utilizzato se LIFO è assolutamente necessario, poiché a causa della bidirezionalità dei nodi, questa classe perde la metà delle prestazioni rispetto 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();
           }
       }
   }
}

Code di blocco

Interfaccia BlockingQueue<E> : se sono presenti molti dati, ConcurrentLinkedQueue non è sufficiente.

Quando i thread non riescono a svolgere il loro lavoro, puoi facilmente ottenere un OutOfMemmoryException . E affinché tali casi non si verifichino, abbiamo un BlockingQueue per lavorare con la presenza di diversi metodi per riempire e lavorare con la coda e i blocchi condizionali.

BlockingQueue non riconosce gli elementi null e genera un'eccezione NullPointerException quando si tenta di aggiungere o ottenere tale elemento. Il metodo poll restituisce un elemento null se nessun elemento è stato inserito nella coda entro il timeout.

BlockingQueue<E> implementazioni

Diamo un'occhiata più da vicino a ciascuna delle nostre implementazioni di BlockingQueue :

ArrayBlockingQueue<E> è una classe di coda di blocco basata sul classico ring buffer. Qui abbiamo la possibilità di gestire l'“onestà” delle serrature. Se fair=false (impostazione predefinita), l'ordinamento dei thread non è garantito.

DelayQueue<E extends Delayed> è una classe che consente di estrarre elementi dalla coda solo dopo un certo ritardo, definito in ciascun elemento tramite il metodo getDelay dell'interfaccia Delayed .

LinkedBlockingQueue<E> è una coda di blocco sui nodi collegati, implementata sull'algoritmo "two lock queue": il primo blocco è per l'aggiunta, il secondo è per estrarre un elemento dalla coda. A causa dei blocchi, rispetto a ArrayBlockingQueue , questa classe ha prestazioni elevate, ma richiede più memoria. La dimensione della coda viene impostata tramite il costruttore ed è uguale a Integer.MAX_VALUE per impostazione predefinita.

PriorityBlockingQueue<E> è un wrapper multithread su PriorityQueue . Il comparatore è responsabile della logica con cui verrà aggiunto l'elemento. L'elemento più piccolo viene fuori per primo.

SynchronousQueue<E> - la coda funziona secondo il principio FIFO (first-in-first-out). Ogni operazione di inserimento blocca il thread "Producer" fino a quando il thread "Consumer" estrae l'elemento dalla coda e viceversa, il "Consumer" attenderà fino a quando il "Producer" inserisce l'elemento.

BlockingDeque<E> è un'interfaccia che descrive metodi aggiuntivi per una coda di blocco bidirezionale. I dati possono essere inseriti ed estratti da entrambi i lati della coda.

LinkedBlockingDeque<E> è una coda di blocco bidirezionale sui nodi collegati, implementata come un semplice elenco bidirezionale con un blocco. La dimensione della coda viene impostata tramite il costruttore ed è uguale a Integer.MAX_VALUE per impostazione predefinita.

TransferQueue<E> - l'interfaccia è interessante in quanto quando un elemento viene aggiunto alla coda, è possibile bloccare l'inserimento del thread Producer fino a quando un altro thread Consumer estrae l'elemento dalla coda. È inoltre possibile aggiungere un controllo per un timeout specifico o impostare un controllo per i consumatori in sospeso . Di conseguenza, otteniamo un meccanismo di trasferimento dati con supporto per messaggi asincroni e sincroni.

LinkedTransferQueue<E> è un'implementazione di TransferQueue basata sull'algoritmo Dual Queues con Slack. Fa un uso intenso di CAS (vedi sopra) e parcheggio thread quando è inattivo.