Code non bloccanti
![](https://cdn.codegym.cc/images/article/128e424c-63bb-47db-9d23-7ce3ae97fa4a/512.jpeg)
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
![](https://cdn.codegym.cc/images/article/8c8152d6-ffe6-4165-879c-3bcae893696f/1080.jpeg)
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.
GO TO FULL VERSION