LinkedBlockingQueue vs ConcurrentLinkedQueue

1. Introduktion

LinkedBlockingQueue og ConcurrentLinkedQueue er de to mest anvendte samtidige køer i Java. Selvom begge køer ofte bruges som en samtidig datastruktur, er der subtile karakteristika og adfærdsmæssige forskelle mellem dem.

I denne korte vejledning diskuterer vi begge disse køer og forklarer deres ligheder og forskelle.

2. LinkedBlockingQueue

Det LinkedBlockingQueue er en valgfrit afgrænset blokering af køimplementering, hvilket betyder, at køstørrelsen kan angives, hvis det er nødvendigt.

Lad os oprette en LinkedBlockingQueue som kan indeholde op til 100 elementer:

BlockingQueue boundedQueue = ny LinkedBlockingQueue (100);

Vi kan også oprette en ubegrænset LinkedBlockingQueue bare ved ikke at angive størrelsen:

BlockingQueue ubegrænsetQueue = ny LinkedBlockingQueue ();

En ubegrænset kø indebærer, at køens størrelse ikke er specificeret under oprettelsen. Derfor kan køen vokse dynamisk, når der føjes elementer til den. Men hvis der ikke er nogen hukommelse tilbage, kaster køen a java.lang.OutOfMemoryError.

Vi kan oprette en LinkedBlockingQueue også fra en eksisterende samling:

Collection listOfNumbers = Arrays.asList (1,2,3,4,5); BlockingQueue queue = new LinkedBlockingQueue (listOfNumbers);

Det LinkedBlockingQueue klasse implementerer BlockingQueue interface, der giver den blokerende natur til den.

En blokerende kø indikerer, at køen blokerer den adgangstråd, hvis den er fuld (når køen er afgrænset) eller bliver tom. Hvis køen er fuld, vil tilføjelse af et nyt element blokere adgangstråden, medmindre der er plads til det nye element. Tilsvarende, hvis køen er tom, blokerer adgang til et element den kaldende tråd:

ExecutorService executorService = Executors.newFixedThreadPool (1); LinkedBlockingQueue kø = ny LinkedBlockingQueue (); executorService.submit (() -> {prøv {queue.take ();} fangst (InterruptedException e) {// undtagelseshåndtering}});

I ovenstående kodestykke har vi adgang til en tom kø. Derfor er den tage metode blokerer opkaldstråden.

Den blokerende funktion af LinkedBlockingQueue er forbundet med nogle omkostninger. Disse omkostninger er fordi hver sætte eller den tage betjening er lås bestridt mellem producenten eller forbrugertrådene. Derfor, i scenarier med mange producenter og forbrugere, sætte og tage handlinger kunne være langsommere.

3. ConcurrentLinkedQueue

EN ConcurrentLinkedQueueer en ubegrænset, trådsikker og ikke-blokerende kø.

Lad os oprette en tom ConcurrentLinkedQueue:

ConcurrentLinkedQueue kø = ny ConcurrentLinkedQueue ();

Vi kan oprette en ConcurrentLinkedQueue også fra en eksisterende samling:

Collection listOfNumbers = Arrays.asList (1,2,3,4,5); ConcurrentLinkedQueue kø = ny ConcurrentLinkedQueue (listOfNumbers);

I modsætning til en LinkedBlockingQueue,-en ConcurrentLinkedQueue er en ikke-blokerende kø. Således blokerer den ikke en tråd, når køen er tom. I stedet vender det tilbage nul. Da det er ubegrænset, kaster det et java.lang.OutOfMemoryError hvis der ikke er ekstra hukommelse til at tilføje nye elementer.

Bortset fra at være ikke-blokerende, a ConcurrentLinkedQueue har yderligere funktionalitet.

I ethvert producent-forbrugerscenarie vil forbrugere ikke være tilfredse med producenterne; dog vil flere producenter kæmpe med hinanden:

int-element = 1; ExecutorService executorService = Executors.newFixedThreadPool (2); ConcurrentLinkedQueue kø = ny ConcurrentLinkedQueue (); Kørbar offerTask = () -> queue.offer (element); Kan kaldes pollTask ​​= () -> {mens (queue.peek ()! = Null) {return queue.poll (). IntValue (); } returnere null; }; executorService.submit (offerTask); Future ReturnElement = executorService.submit (pollTask); assertThat (ReturnElement.get (). intValue (), er (equalTo (element))); 

Den første opgave, offerTask, tilføjer et element til køen, og den anden opgave, pollTask, hente et element fra køen. Afstemningsopgaven derudover kontrollerer køen for et element først som ConcurrentLinkedQueue er ikke-blokerende og kan returnere en nul værdi.

4. Ligheder

Begge LinkedBlockingQueue og ConcurrentLinkedQueue er køimplementeringer og deler nogle fælles egenskaber. Lad os diskutere lighederne mellem disse to køer:

  1. Begge implementerer Interface
  2. Dem begge brug sammenkædede noder at gemme deres elementer
  3. Begge er velegnede til samtidige adgangsscenarier

5. Forskelle

Selvom begge disse køer har visse ligheder, er der også væsentlige karakteristiske forskelle:

FunktionLinkedBlockingQueueConcurrentLinkedQueue
Blokerende naturDet er en blokerende kø og implementerer BlockingQueue interfaceDet er en ikke-blokerende kø og implementerer ikke BlockingQueue interface
KøstørrelseDet er en valgfri afgrænset kø, hvilket betyder, at der er bestemmelser til at definere køstørrelsen under oprettelsenDet er en ubegrænset kø, og der er ingen mulighed for at specificere køstørrelsen under oprettelsen
Låser naturendet er en låsebaseret kødet er en låsfri kø
AlgoritmeDet implementerer dens låsning er baseret på to-lås kø algoritmeDet er afhængig af Michael & Scott algoritme til ikke-blokerende, låsefri køer
ImplementeringI to-lås kø algoritmemekanisme, LinkedBlockingQueue bruger to forskellige låse - putLock og takeLock . Det sæt / tag operationer bruger den første låsetype, og tage / afstemning operationer bruger den anden låsetype Det bruger CAS (Compare-And-Swap)) til dets aktiviteter
Blokerende adfærdDet er en blokerende kø. Så det blokerer adgangstrådene, når køen er tomDet blokerer ikke adgangstråden, når køen er tom og vender tilbage nul

6. Konklusion

I denne artikel lærte vi om LinkedBlockingQueue og ConcurrentLinkedQueue.

For det første diskuterede vi disse to køimplementeringer individuelt og nogle af deres egenskaber. Derefter så vi lighederne mellem disse to køimplementeringer. Endelig undersøgte vi forskellene mellem disse to køimplementeringer.

Som altid er kildekoden til eksemplerne tilgængelig på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found