Icke-blockerande köer

Trådsäkra och viktigast av allt icke-blockerande köimplementeringar på länkade noder.

ConcurrentLinkedQueue<E> - den använder en väntefri algoritm anpassad för att fungera med sophämtaren. Denna algoritm är ganska effektiv och mycket snabb, eftersom den är byggd på CAS. Metoden size() kan köras under lång tid, så det är bäst att inte dra den hela tiden.

ConcurrentLinkedDeque<E> - Deque står för Double ended queue. Detta innebär att data kan läggas till och dras från båda sidor. Följaktligen stöder klassen båda driftsätten: FIFO (First In First Out) och LIFO (Last In First Out).

I praktiken bör ConcurrentLinkedDeque användas om LIFO är absolut nödvändigt, eftersom på grund av nodernas dubbelriktade egenskaper förlorar denna klass hälften i prestanda jämfört med 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();
           }
       }
   }
}

Blockerande köer

BlockingQueue<E> -gränssnitt - om det finns mycket data räcker det inte med ConcurrentLinkedQueue .

När trådar inte gör sitt jobb kan du enkelt få en OutOfMemmoryException . Och för att sådana fall inte ska uppstå har vi en BlockingQueue för arbete med förekomsten av olika metoder för att fylla och arbeta med kön och villkorliga lås.

BlockingQueue känner inte igen null-element och kastar ett NullPointerException när man försöker lägga till eller hämta ett sådant element. Avfrågningsmetoden returnerar ett null-element om inget element har placerats i kön inom timeout.

BlockingQueue<E>-implementationer

Låt oss ta en närmare titt på var och en av våra BlockingQueue- implementeringar :

ArrayBlockingQueue<E> är en blockerande köklass byggd på den klassiska ringbufferten. Här har vi möjlighet att hantera låsens ”ärlighet”. Om fair=false (standard) garanteras inte trådordning.

DelayQueue<E extends Delayed> är en klass som låter dig dra element från kön först efter en viss fördröjning, definierad i varje element genom metoden getDelay i Delayed - gränssnittet.

LinkedBlockingQueue<E> är en blockeringskö på länkade noder, implementerad på algoritmen "två låsköer": det första låset är för att lägga till, det andra är för att dra ett element från kön. På grund av lås, jämfört med ArrayBlockingQueue , har denna klass hög prestanda, men den kräver mer minne. Köstorleken ställs in via konstruktorn och är lika med Integer.MAX_VALUE som standard.

PriorityBlockingQueue<E> är ett flertrådigt omslag över PriorityQueue . Komparatorn är ansvarig för logiken med vilken elementet kommer att läggas till. Det minsta elementet kommer ut först.

SynchronousQueue<E> - kön fungerar enligt FIFO-principen (först-in-först-ut). Varje infogningsoperation blockerar "Producer"-tråden tills "Consumer"-tråden drar elementet från kön och vice versa, "Consumer" väntar tills "Producer" infogar elementet.

BlockingDeque<E> är ett gränssnitt som beskriver ytterligare metoder för en dubbelriktad blockeringskö. Data kan infogas och dras ut från båda sidor av kön.

LinkedBlockingDeque<E> är en dubbelriktad blockeringskö på länkade noder, implementerad som en enkel dubbelriktad lista med ett lås. Köstorleken ställs in via konstruktorn och är lika med Integer.MAX_VALUE som standard.

TransferQueue<E> - gränssnittet är intressant genom att när ett element läggs till i kön är det möjligt att blockera den infogar Producer- tråden tills en annan Konsument- tråd drar elementet från kön. Du kan också lägga till en check för en specifik timeout eller sätta en check för väntande konsumenter . Som ett resultat får vi en dataöverföringsmekanism med stöd för asynkrona och synkrona meddelanden.

LinkedTransferQueue<E> är en implementering av TransferQueue baserad på Dual Queue with Slack-algoritmen. Använder mycket CAS (se ovan) och gängparkering vid tomgång.