Ikke-blokerende køer

Trådsikre og vigtigst af alt ikke-blokerende -implementeringer på sammenkædede noder.

ConcurrentLinkedQueue<E> - den bruger en ventefri algoritme tilpasset til at arbejde med skraldeopsamleren. Denne algoritme er ret effektiv og meget hurtig, da den er bygget på CAS. Size()- metodenkan køre i lang tid, så det er bedst ikke at trække den hele tiden.

ConcurrentLinkedDeque<E> - Deque står for Double ended queue. Det betyder, at data kan tilføjes og trækkes fra begge sider. Følgelig understøtter klassen begge driftstilstande: FIFO (First In First Out) og LIFO (Last In First Out).

I praksis bør ConcurrentLinkedDeque bruges, hvis LIFO er absolut nødvendigt, da denne klasse på grund af nodernes todirektionalitet mister halvdelen i ydeevne sammenlignet 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();
           }
       }
   }
}

Blokering af køer

BlockingQueue<E> interface - hvis der er mange data, er ConcurrentLinkedQueue ikke nok.

Når tråde ikke klarer deres arbejde, kan du nemt få en OutOfMemmoryException . Og for at sådanne sager ikke opstår, har vi en BlockingQueue til arbejde med tilstedeværelsen af ​​forskellige metoder til at udfylde og arbejde med køen og betingede låse.

BlockingQueue genkender ikke null-elementer og kaster en NullPointerException , når du forsøger at tilføje eller hente et sådant element. Afstemningsmetoden returnerer et null-element, hvis intet element er blevet placeret i køen inden for timeout.

BlockingQueue<E>-implementeringer

Lad os se nærmere på hver af vores BlockingQueue- implementeringer :

ArrayBlockingQueue<E> er en blokerende køklasse bygget på den klassiske ringbuffer. Her har vi mulighed for at styre låses “ærlighed”. Hvis fair=false (standard), så er trådbestilling ikke garanteret.

DelayQueue<E extends Delayed> er en klasse, der giver dig mulighed for kun at trække elementer fra køen efter en vis forsinkelse, defineret i hvert element gennem getDelay- metoden i Delayed- grænsefladen.

LinkedBlockingQueue<E> er en blokeringskø på sammenkædede noder, implementeret på "two lock queue"-algoritmen: den første lås er til at tilføje, den anden er til at trække et element fra køen. På grund af låse, sammenlignet med ArrayBlockingQueue , har denne klasse høj ydeevne, men den kræver mere hukommelse. Køstørrelsen indstilles via konstruktøren og er som standard lig med Integer.MAX_VALUE.

PriorityBlockingQueue<E> er en multi-threaded wrapper over PriorityQueue . Komparatoren er ansvarlig for logikken, hvorved elementet tilføjes. Det mindste element kommer først ud.

SynchronousQueue<E> - køen fungerer efter FIFO (first-in-first-out) princippet. Hver indsættelsesoperation blokerer "Producer"-tråden, indtil "Forbruger"-tråden trækker elementet fra køen og omvendt, vil "Forbruger" vente, indtil "Producer" indsætter elementet.

BlockingDeque<E> er en grænseflade, der beskriver yderligere metoder til en tovejs blokeringskø. Data kan indsættes og trækkes ud fra begge sider af køen.

LinkedBlockingDeque<E> er en tovejs blokeringskø på sammenkædede noder, implementeret som en simpel tovejsliste med én lås. Køstørrelsen indstilles via konstruktøren og er som standard lig med Integer.MAX_VALUE.

TransferQueue<E> - grænsefladen er interessant ved, at når et element tilføjes til køen, er det muligt at blokere den indsættende Producer- tråd , indtil en anden forbrugertråd trækker elementet fra køen. Du kan også tilføje en check for en bestemt timeout eller indstille en check for ventende forbrugere . Som et resultat får vi en dataoverførselsmekanisme med understøttelse af asynkrone og synkrone beskeder.

LinkedTransferQueue<E> er en implementering af TransferQueue baseret på Dual Queue med Slack-algoritmen. Gør stor brug af CAS (se ovenfor) og gevindparkering i tomgang.