LinkedBlockingQueue vs ConcurrentLinkedQueue
1. Introduksjon
LinkedBlockingQueue og ConcurrentLinkedQueue er de to mest brukte samtidskøene i Java. Selv om begge køene ofte brukes som samtidig datastruktur, er det subtile egenskaper og atferdsforskjeller mellom dem.
I denne korte veiledningen vil vi diskutere begge disse køene og forklare deres likheter og forskjeller.
2. LinkedBlockingQueue
De LinkedBlockingQueue er en valgfritt avgrenset blokkering av køimplementering, noe som betyr at køstørrelsen kan spesifiseres ved behov.
La oss lage en LinkedBlockingQueue som kan inneholde opptil 100 elementer:
BlockingQueue boundedQueue = ny LinkedBlockingQueue (100);
Vi kan også lage en ubegrenset LinkedBlockingQueue bare ved ikke å spesifisere størrelsen:
BlockingQueue unboundedQueue = ny LinkedBlockingQueue ();
En ubegrenset kø innebærer at størrelsen på køen ikke er spesifisert mens du oppretter. Derfor kan køen vokse dynamisk ettersom elementer blir lagt til den. Men hvis det ikke er noe minne igjen, kaster køen a java.lang.OutOfMemoryError.
Vi kan lage en LinkedBlockingQueue fra en eksisterende samling også:
Collection listOfNumbers = Arrays.asList (1,2,3,4,5); BlockingQueue queue = new LinkedBlockingQueue (listOfNumbers);
De LinkedBlockingQueue klasse implementerer BlockingQueue grensesnitt, som gir den blokkerende naturen til den.
En blokkeringskø indikerer at køen blokkerer tilgangstråden hvis den er full (når køen er avgrenset) eller blir tom. Hvis køen er full, vil legging av et nytt element blokkere tilgangstråden med mindre det er plass tilgjengelig for det nye elementet. Tilsvarende, hvis køen er tom, vil tilgang til et element blokkere ringetråden:
ExecutorService executorService = Executors.newFixedThreadPool (1); LinkedBlockingQueue kø = ny LinkedBlockingQueue (); executorService.submit (() -> {prøv {queue.take ();} fangst (InterruptedException e) {// unntakshåndtering}});
I kodebiten ovenfor får vi tilgang til en tom kø. derfor ta metoden blokkerer ringetråden.
Blokkeringsfunksjonen til LinkedBlockingQueue er forbundet med noen kostnader. Denne kostnaden er fordi hver sette eller ta operasjonen er i strid mellom produsenten eller forbrukertrådene. Derfor, i scenarier med mange produsenter og forbrukere, sette og iverksette tiltak kan være tregere.
3. ConcurrentLinkedQueue
EN ConcurrentLinkedQueueer en ubegrenset, trådsikker og ikke-blokkerende kø.
La oss lage en tom ConcurrentLinkedQueue:
ConcurrentLinkedQueue kø = ny ConcurrentLinkedQueue ();
Vi kan lage en ConcurrentLinkedQueue fra en eksisterende samling også:
Collection listOfNumbers = Arrays.asList (1,2,3,4,5); ConcurrentLinkedQueue kø = ny ConcurrentLinkedQueue (listOfNumbers);
I motsetning til en LinkedBlockingQueue,en ConcurrentLinkedQueue er en ikke-blokkerende kø. Dermed blokkerer den ikke en tråd når køen er tom. I stedet kommer den tilbake null. Siden det er ubegrenset, vil det kaste et java.lang.OutOfMemoryError hvis det ikke er noe ekstra minne for å legge til nye elementer.
Bortsett fra å være ikke-blokkerende, a ConcurrentLinkedQueue har ekstra funksjonalitet.
I ethvert produsent-forbrukerscenario vil forbrukere ikke nøye seg med produsenter; imidlertid vil flere produsenter kjempe med hverandre:
int element = 1; ExecutorService executorService = Executors.newFixedThreadPool (2); ConcurrentLinkedQueue kø = ny ConcurrentLinkedQueue (); Runnable offerTask = () -> queue.offer (element); Kallbar pollTask = () -> {mens (queue.peek ()! = Null) {return queue.poll (). IntValue (); } returner null; }; executorService.submit (offerTask); Future returnElement = executorService.submit (pollTask); assertThat (returnElement.get (). intValue (), er (equalTo (element)));
Den første oppgaven, offerTask, legger til et element i køen, og den andre oppgaven, pollTask, hente et element fra køen. Avstemningsoppgaven i tillegg sjekker køen for et element først som ConcurrentLinkedQueue er ikke-blokkerende og kan returnere a null verdi.
4. Likheter
Både LinkedBlockingQueue og ConcurrentLinkedQueue er køimplementeringer og deler noen vanlige egenskaper. La oss diskutere likhetene mellom disse to køene:
- Både implementerer Kø Grensesnitt
- De begge bruk koblede noder for å lagre elementene sine
- Både er egnet for samtidige tilgangsscenarier
5. Forskjeller
Selv om begge disse køene har visse likheter, er det også store karakteristiske forskjeller:
Trekk | LinkedBlockingQueue | ConcurrentLinkedQueue |
---|---|---|
Blokkerer naturen | Det er en blokkerende kø og implementerer BlockingQueue grensesnitt | Det er en ikke-blokkerende kø og implementerer ikke BlockingQueue grensesnitt |
Køstørrelse | Det er en valgfri avgrenset kø, noe som betyr at det er bestemmelser for å definere køstørrelsen under opprettelsen | Det er en ubegrenset kø, og det er ingen bestemmelse for å spesifisere køstørrelsen under opprettelsen |
Låser naturen | Det er en låsebasert kø | Det er en låsfri kø |
Algoritme | Den implementerer dens låsing basert på to-låskø algoritme | Det er avhengig av Michael & Scott algoritme for ikke-blokkerende, låsfrie køer |
Gjennomføring | I to-låskø algoritmemekanisme, LinkedBlockingQueue bruker to forskjellige låser - putLock og takeLock . De sette / ta operasjoner bruker den første låsetypen, og ta / avstem operasjoner bruker den andre låsetypen | Den bruker CAS (Compare-And-Swap)) for sin virksomhet |
Blokkerende atferd | Det er en blokkerende kø. Så det blokkerer tilgangstrådene når køen er tom | Det blokkerer ikke tilgangstråden når køen er tom og returnerer null |
6. Konklusjon
I denne artikkelen lærte vi om LinkedBlockingQueue og ConcurrentLinkedQueue.
Først diskuterte vi individuelt disse to køimplementeringene og noen av deres egenskaper. Så så vi likhetene mellom disse to køimplementeringene. Til slutt undersøkte vi forskjellene mellom disse to køimplementeringene.
Som alltid er kildekoden til eksemplene tilgjengelig på GitHub.