LinkedBlockingQueue versus ConcurrentLinkedQueue

1. Inleiding

LinkedBlockingQueue en ConcurrentLinkedQueue zijn de twee meest gebruikte gelijktijdige wachtrijen in Java. Hoewel beide wachtrijen vaak worden gebruikt als een gelijktijdige datastructuur, zijn er subtiele kenmerken en gedragsverschillen tussen beide.

In deze korte tutorial bespreken we beide wachtrijen en leggen we hun overeenkomsten en verschillen uit.

2. LinkedBlockingQueue

De LinkedBlockingQueue is een optioneel begrensd wachtrij-implementatie blokkeren, wat betekent dat de wachtrijgrootte indien nodig kan worden gespecificeerd.

Laten we een LinkedBlockingQueue die maximaal 100 elementen kan bevatten:

BlockingQueue boundedQueue = nieuwe LinkedBlockingQueue (100);

We kunnen ook een onbegrensd LinkedBlockingQueue gewoon door de maat niet op te geven:

BlockingQueue unboundedQueue = nieuwe LinkedBlockingQueue ();

Een onbegrensde wachtrij houdt in dat de grootte van de wachtrij niet wordt opgegeven tijdens het maken. Daarom kan de wachtrij dynamisch groeien naarmate er elementen aan worden toegevoegd. Als er echter geen geheugen meer is, gooit de wachtrij een java.lang.OutOfMemoryError.

We kunnen een LinkedBlockingQueue ook uit een bestaande collectie:

Verzameling listOfNumbers = Arrays.asList (1,2,3,4,5); BlockingQueue-wachtrij = nieuwe LinkedBlockingQueue (listOfNumbers);

De LinkedBlockingQueue klasse implementeert het BlockingQueue interface, die het blokkerende karakter ervan biedt.

Een blokkerende wachtrij geeft aan dat de wachtrij de toegang tot de thread blokkeert als deze vol is (wanneer de wachtrij begrensd is) of leeg raakt. Als de wachtrij vol is, zal het toevoegen van een nieuw element de toegang tot de thread blokkeren, tenzij er ruimte beschikbaar is voor het nieuwe element. Evenzo, als de wachtrij leeg is, blokkeert toegang tot een element de aanroepende thread:

ExecutorService executorService = Executors.newFixedThreadPool (1); LinkedBlockingQueue wachtrij = nieuwe LinkedBlockingQueue (); executorService.submit (() -> {probeer {queue.take ();} catch (InterruptedException e) {// afhandeling van uitzonderingen}});

In het bovenstaande codefragment hebben we toegang tot een lege wachtrij. Daarom, de nemen methode blokkeert de aanroepende thread.

De blokkeerfunctie van het LinkedBlockingQueue gaat gepaard met een aantal kosten. Deze kosten zijn omdat elk leggen of de nemen werking is een vergrendeling die wordt betwist tussen de producent of de consumentendraden. Daarom, in scenario's met veel producenten en consumenten, leggen en acties ondernemen kan langzamer zijn.

3. ConcurrentLinkedQueue

EEN ConcurrentLinkedQueueis een onbegrensde, threadveilige en niet-blokkerende wachtrij.

Laten we een leeg maken ConcurrentLinkedQueue:

ConcurrentLinkedQueue wachtrij = nieuwe ConcurrentLinkedQueue ();

We kunnen een ConcurrentLinkedQueue ook uit een bestaande collectie:

Verzameling listOfNumbers = Arrays.asList (1,2,3,4,5); ConcurrentLinkedQueue wachtrij = nieuwe ConcurrentLinkedQueue (listOfNumbers);

In tegenstelling tot een LinkedBlockingQueue,een ConcurrentLinkedQueue is een niet-blokkerende wachtrij. Het blokkeert dus geen thread als de wachtrij leeg is. In plaats daarvan keert het terug nul. Omdat het onbegrensd is, gooit het een java.lang.OutOfMemoryError als er geen extra geheugen is om nieuwe elementen toe te voegen.

Behalve dat het niet-blokkerend is, is een ConcurrentLinkedQueue heeft extra functionaliteit.

In elk producent-consument-scenario zullen consumenten niet tevreden zijn met producenten; meerdere producenten zullen echter met elkaar strijden:

int element = 1; ExecutorService executorService = Executors.newFixedThreadPool (2); ConcurrentLinkedQueue wachtrij = nieuwe ConcurrentLinkedQueue (); Runnable offerTask = () -> queue.offer (element); Oproepbare pollTask ​​= () -> {while (queue.peek ()! = Null) {return queue.poll (). IntValue (); } retourneer null; }; executorService.submit (offerTask); Future returnElement = executorService.submit (pollTask); assertThat (geretourneerdeElement.get (). intValue (), is (equalTo (element))); 

De eerste taak, offerTask, voegt een element toe aan de wachtrij, en de tweede taak, pollTask, haal een element uit de wachtrij. De poll-taak bovendien controleert de wachtrij eerst op een element als ConcurrentLinkedQueue is niet-blokkerend en kan een nul waarde.

4. Overeenkomsten

Beide LinkedBlockingQueue en de ConcurrentLinkedQueue zijn wachtrij-implementaties en delen enkele gemeenschappelijke kenmerken. Laten we de overeenkomsten van deze twee wachtrijen bespreken:

  1. Beide implementeert het Wachtrij Koppel
  2. Zij allebei gebruik gekoppelde knooppunten om hun elementen op te slaan
  3. Beide zijn geschikt voor scenario's voor gelijktijdige toegang

5. Verschillen

Hoewel beide wachtrijen bepaalde overeenkomsten vertonen, zijn er ook aanzienlijke verschillen in kenmerken:

Voorzien zijn vanLinkedBlockingQueueConcurrentLinkedQueue
De natuur blokkerenHet is een blokkerende wachtrij en implementeert de BlockingQueue koppelHet is een niet-blokkerende wachtrij en implementeert de BlockingQueue koppel
WachtrijgrootteHet is een optioneel begrensde wachtrij, wat betekent dat er voorzieningen zijn om de wachtrijgrootte te definiëren tijdens het makenHet is een onbegrensde wachtrij en er is geen voorziening om de wachtrijgrootte op te geven tijdens het maken
Vergrendeling van de natuurHet is een wachtrij op basis van een slotHet is een wachtrij zonder slot
AlgoritmeHet implementeert zijn vergrendeling gebaseerd op wachtrij met twee vergrendelingen algoritmeHet is gebaseerd op de Michael & Scott-algoritme voor niet-blokkerende wachtrijen zonder vergrendeling
ImplementatieIn de wachtrij met twee vergrendelingen algoritme mechanisme, LinkedBlockingQueue gebruikt twee verschillende sloten - de putLock en de takeLock . De zetten / nemen bewerkingen gebruikt het eerste vergrendeltype en de nemen / poll operaties gebruiken het andere type vergrendeling Het maakt gebruik van CAS (Compare-And-Swap) voor zijn activiteiten
Blokkerend gedragHet is een blokkerende wachtrij. Het blokkeert dus de toegang tot threads wanneer de wachtrij leeg isHet blokkeert de thread die toegang heeft niet wanneer de wachtrij leeg is en terugkeert nul

6. Conclusie

In dit artikel hebben we geleerd over LinkedBlockingQueue en ConcurrentLinkedQueue.

Eerst hebben we deze twee wachtrij-implementaties en enkele van hun kenmerken afzonderlijk besproken. Vervolgens zagen we de overeenkomsten tussen deze twee wachtrij-implementaties. Ten slotte hebben we de verschillen tussen deze twee wachtrij-implementaties onderzocht.

Zoals altijd is de broncode van de voorbeelden beschikbaar op GitHub.


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