Blockingqueue en de Implementaties Ervan
Basisimplementaties van BlockingQueue
We zullen niet elke realisatie in detail bespreken, omdat dit veel tijd zou kosten en het onwaarschijnlijk is dat je ze allemaal nodig hebt. We behandelen de algemene concepten en welke constructors ze hebben.
Praktijkvoorbeeld
Stel je een fabriek voor waar één thread, de producer, onderdelen maakt en een andere thread, de consumer, deze verwerkt. De producer plaatst de onderdelen in een queue, terwijl de consumer ze uit de queue haalt en verwerkt. Als de queue leeg is, wacht de consumer tot de producer nieuwe onderdelen toevoegt. Omgekeerd, als de queue vol is, wacht de producer tot de consumer ruimte maakt.
Iets verderop zullen we deze taak implementeren in code.
Verschillen met andere collectietypen
De BlockingQueue biedt geautomatiseerde synchronisatie, waarbij de toegang van threads tot de queue wordt beheerd zonder dat handmatige synchronisatie nodig is. Daarnaast ondersteunt het blokkerende bewerkingen voor het toevoegen en ophalen van items, een functie die niet voorkomt bij andere collecties zoals ArrayList of LinkedList.
BlockingQueue-implementaties
ArrayBlockingQueue: Een groottebeperkte queue die een array gebruikt om items op te slaan.
Main.java
123456789// Constructor with fixed capacity BlockingQueue<String> queue1 = new ArrayBlockingQueue<>(5); // Constructor with fixed capacity and fair access BlockingQueue<String> queue2 = new ArrayBlockingQueue<>(5, true); // Constructor with fixed capacity and initial collection of elements Collection<String> initialElements = java.util.Arrays.asList("One", "Two", "Three"); BlockingQueue<String> queue3 = new ArrayBlockingQueue<>(5, false, initialElements);
De parameter true activeert een eerlijk toegangsbeleid door een FIFO-volgorde voor threadtoegang te bieden.
LinkedBlockingQueueue: Een wachtrij gebaseerd op gekoppelde knooppunten die beperkt of onbeperkt kan zijn.
Main.java
123456789// Constructor without capacity bounds BlockingQueue<String> queue1 = new LinkedBlockingQueue<>(); // Constructor with fixed capacity BlockingQueue<String> queue2 = new LinkedBlockingQueue<>(5); // Constructor with initial collection of elements Collection<String> initialElements = java.util.Arrays.asList("One", "Two", "Three"); BlockingQueue<String> queue3 = new LinkedBlockingQueue<>(initialElements);
PriorityBlockingQueue: Een onbegrensde geprioriteerde wachtrij waarbij elementen worden opgehaald volgens hun natuurlijke volgorde of zoals gespecificeerd door een comparator.
Main.java
12345678910111213// Constructor without initial capacity (default is 11) BlockingQueue<Integer> queue1 = new PriorityBlockingQueue<>(); // Constructor with initial capacity BlockingQueue<Integer> queue2 = new PriorityBlockingQueue<>(5); // Constructor with initial capacity and comparator Comparator<Integer> comparator = Integer::compareTo; BlockingQueue<Integer> queue3 = new PriorityBlockingQueue<>(5, comparator); // Constructor with initial collection of elements Collection<Integer> initialElements = java.util.Arrays.asList(1, 3, 2); BlockingQueue<Integer> queue4 = new PriorityBlockingQueue<>(initialElements)
DelayQueue: Een vertraagde wachtrij waarbij items pas kunnen worden opgehaald nadat hun vertraging is verlopen.
DelayedElement.java
DelayQueueConstructors.java
123456789101112131415161718class DelayedElement implements Delayed { private final long expirationTime; // The time when the element will be available public DelayedElement(long delay, TimeUnit unit) { this.expirationTime = System.currentTimeMillis() + unit.toMillis(delay); } @Override public long getDelay(TimeUnit unit) { long delay = expirationTime - System.currentTimeMillis(); // Calculate the remaining delay return unit.convert(delay, TimeUnit.MILLISECONDS); // Convert the delay to the specified time unit } @Override public int compareTo(Delayed o) { return Long.compare(this.expirationTime, ((DelayedElement) o).expirationTime); } }
Deze code toont het gebruik van de DelayedElement klasse, die implementeert het Delayed interface, en de DelayQueue wachtrij in Java. De DelayedElement klasse definieert een getDelay methode om de resterende vertragingstijd te berekenen en een compareTo methode om objecten te vergelijken op basis van de verloopdatum van de vertraging.
De main methode maakt twee wachtrijen aan: queue1, een lege wachtrij, en queue2, een wachtrij die is geïnitialiseerd met elementen die respectievelijk een vertraging van 5 en 1 seconde hebben.
De items in DelayQueueue worden beschikbaar voor ophalen nadat de opgegeven vertragingstijd is verstreken.
SynchronousQueueue: Een wachtrij zonder capaciteit, waarbij elke invoegbewerking moet wachten op de overeenkomstige extractiebewerking en omgekeerd.
Main.java
12345// Constructor without fair access BlockingQueue<String> queue1 = new SynchronousQueue<>(); // Constructor with fair access BlockingQueue<String> queue2 = new SynchronousQueue<>(true);
De belangrijkste methoden van de BlockingQueue:
Elementen toevoegen:
De void put(E e) methode voegt een item toe aan de wachtrij en blokkeert de thread als de wachtrij vol is. Als alternatief probeert de boolean offer(E e, long timeout, TimeUnit unit) methode een item toe te voegen aan de wachtrij, waarbij wordt gewacht gedurende de opgegeven tijd als de wachtrij vol is.
Main.java
1234567891011121314151617public class BlockingQueueExample { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<>(2); try { queue.put("Element 1"); // Insert the first element, no blocking. queue.put("Element 2"); // Insert the second element, no blocking. // Try to add the third element with a 2-second timeout. // Since the queue is full, it will wait for 2 seconds. boolean success = queue.offer("Element 3", 2, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } } }
Dit voorbeeld toont het invoegen van twee elementen in een BlockingQueue zonder blokkering, gevolgd door een poging om een derde element toe te voegen met een timeout van 2 seconden via de offer()-methode, die zal wachten als de wachtrij vol is.
Ophalen van elementen:
De methode E take() haalt een item uit de wachtrij en retourneert deze, waarbij de thread wordt geblokkeerd als de wachtrij leeg is. Als alternatief probeert de methode E poll(long timeout, TimeUnit unit) een item uit de wachtrij te halen, waarbij wordt gewacht gedurende de opgegeven tijd als de wachtrij leeg is.
Main.java
1234567891011121314151617181920212223public class BlockingQueueRetrievalExample { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<>(2); try { // Adding elements to the queue queue.put("Element 1"); queue.put("Element 2"); // Retrieve and remove the first element, no blocking since the queue is not empty String item1 = queue.take(); // Returns "Element 1" // Attempt to retrieve and remove the next element with a 2-second timeout String item2 = queue.poll(2, TimeUnit.SECONDS); // Returns "Element 2" // Attempt to retrieve an element when the queue is empty, this will block for 2 seconds String item3 = queue.poll(2, TimeUnit.SECONDS); // Returns `null` after timeout } catch (InterruptedException e) { e.printStackTrace(); } } }
Deze code voegt twee elementen toe aan een BlockingQueue, haalt en verwijdert het eerste element direct, probeert het volgende element op te halen met een timeout van 2 seconden, en probeert tenslotte een element op te halen uit een lege queue, wat resulteert in null na de timeout.
Controleren en verwijderen van elementen:
De boolean remove(Object o) methode verwijdert het gespecificeerde element uit de queue als het aanwezig is. Daarentegen controleert de boolean contains(Object o) methode of het gespecificeerde element aanwezig is in de queue zonder het te verwijderen.
Main.java
1234567891011121314151617181920212223242526public class BlockingQueueCheckRemoveExample { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<>(2); try { // Adding elements to the queue queue.put("Element 1"); queue.put("Element 2"); // Check if "Element 1" is in the queue, should return `true` boolean containsElement1 = queue.contains("Element 1"); // true // Remove "Element 1" from the queue, should return `true` boolean removedElement1 = queue.remove("Element 1"); // true // Check if "Element 1" is still in the queue, should return `false` boolean containsElement1AfterRemoval = queue.contains("Element 1"); // false // Try to remove an element that is not in the queue, should return `false` boolean removedElement3 = queue.remove("Element 3"); // false } catch (InterruptedException e) { e.printStackTrace(); } } }
Deze code voegt twee elementen toe aan een BlockingQueue, controleert op de aanwezigheid van "Element 1", verwijdert dit, controleert opnieuw om de verwijdering te bevestigen en probeert vervolgens een niet-bestaand element te verwijderen.
Controle van de status van de wachtrij:
De methode int size() retourneert het aantal elementen dat zich momenteel in de wachtrij bevindt. Om te bepalen of de wachtrij leeg is, kan de methode boolean isEmpty() worden gebruikt, waarmee wordt gecontroleerd of de wachtrij geen elementen bevat. Voor wachtrijen met een vaste capaciteit geeft de methode int remainingCapacity() het aantal resterende beschikbare plaatsen in de wachtrij aan.
Main.java
123456789101112131415161718192021222324252627282930public class BlockingQueueCapacityExample { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<>(3); try { // Adding elements to the queue queue.put("Element 1"); queue.put("Element 2"); // Get the number of elements in the queue int currentSize = queue.size(); // 2 // Check if the queue is empty boolean isQueueEmpty = queue.isEmpty(); // false // Get the remaining capacity in the queue int remainingSpace = queue.remainingCapacity(); // 1 // Add another element to fill the queue queue.put("Element 3"); // Check the size and remaining capacity after adding the third element currentSize = queue.size(); // 3 remainingSpace = queue.remainingCapacity(); // 0 } catch (InterruptedException e) { e.printStackTrace(); } } }
Deze code voegt elementen toe aan een BlockingQueue, controleert de huidige grootte, verifieert of de wachtrij leeg is, en bepaalt de resterende capaciteit, waarna deze waarden worden bijgewerkt nadat de wachtrij volledig is gevuld.
Een praktijkvoorbeeld in code realiseren
😭 Beperkingen
Een belangrijke beperking is prestatie: door de betrokken vergrendelingsbewerkingen kan de prestatie lager zijn dan bij niet-gesynchroniseerde collecties. Daarnaast kunnen resources een probleem vormen, aangezien grote wachtrijen meer geheugen en CPU-tijd vereisen om de vergrendelingen en synchronisatieprocessen af te handelen.
💪 Voordelen
Aan de positieve kant is het systeem veilig bij multithreading, waardoor veilige communicatie tussen threads mogelijk is zonder handmatig synchronisatiebeheer. Het vereenvoudigt ook de code door complexe synchronisatie- en blokkeringsconstructies te vermijden. Bovendien zorgt de flexibiliteit van verschillende BlockingQueue-implementaties ervoor dat ze geschikt zijn voor diverse gebruiksscenario's.
1. Wat is een BlockingQueue in Java?
2. Wat zijn de belangrijkste methoden van BlockingQueue die een thread blokkeren?
3. Waarvoor is BlockingQueue nuttig in multithreaded applicaties?
Bedankt voor je feedback!
Vraag AI
Vraag AI
Vraag wat u wilt of probeer een van de voorgestelde vragen om onze chat te starten.
What are the main differences between the BlockingQueue implementations?
Can you explain how the DelayQueue works in more detail?
How does the producer-consumer example work in code?
Awesome!
Completion rate improved to 3.33
Blockingqueue en de Implementaties Ervan
Veeg om het menu te tonen
Basisimplementaties van BlockingQueue
We zullen niet elke realisatie in detail bespreken, omdat dit veel tijd zou kosten en het onwaarschijnlijk is dat je ze allemaal nodig hebt. We behandelen de algemene concepten en welke constructors ze hebben.
Praktijkvoorbeeld
Stel je een fabriek voor waar één thread, de producer, onderdelen maakt en een andere thread, de consumer, deze verwerkt. De producer plaatst de onderdelen in een queue, terwijl de consumer ze uit de queue haalt en verwerkt. Als de queue leeg is, wacht de consumer tot de producer nieuwe onderdelen toevoegt. Omgekeerd, als de queue vol is, wacht de producer tot de consumer ruimte maakt.
Iets verderop zullen we deze taak implementeren in code.
Verschillen met andere collectietypen
De BlockingQueue biedt geautomatiseerde synchronisatie, waarbij de toegang van threads tot de queue wordt beheerd zonder dat handmatige synchronisatie nodig is. Daarnaast ondersteunt het blokkerende bewerkingen voor het toevoegen en ophalen van items, een functie die niet voorkomt bij andere collecties zoals ArrayList of LinkedList.
BlockingQueue-implementaties
ArrayBlockingQueue: Een groottebeperkte queue die een array gebruikt om items op te slaan.
Main.java
123456789// Constructor with fixed capacity BlockingQueue<String> queue1 = new ArrayBlockingQueue<>(5); // Constructor with fixed capacity and fair access BlockingQueue<String> queue2 = new ArrayBlockingQueue<>(5, true); // Constructor with fixed capacity and initial collection of elements Collection<String> initialElements = java.util.Arrays.asList("One", "Two", "Three"); BlockingQueue<String> queue3 = new ArrayBlockingQueue<>(5, false, initialElements);
De parameter true activeert een eerlijk toegangsbeleid door een FIFO-volgorde voor threadtoegang te bieden.
LinkedBlockingQueueue: Een wachtrij gebaseerd op gekoppelde knooppunten die beperkt of onbeperkt kan zijn.
Main.java
123456789// Constructor without capacity bounds BlockingQueue<String> queue1 = new LinkedBlockingQueue<>(); // Constructor with fixed capacity BlockingQueue<String> queue2 = new LinkedBlockingQueue<>(5); // Constructor with initial collection of elements Collection<String> initialElements = java.util.Arrays.asList("One", "Two", "Three"); BlockingQueue<String> queue3 = new LinkedBlockingQueue<>(initialElements);
PriorityBlockingQueue: Een onbegrensde geprioriteerde wachtrij waarbij elementen worden opgehaald volgens hun natuurlijke volgorde of zoals gespecificeerd door een comparator.
Main.java
12345678910111213// Constructor without initial capacity (default is 11) BlockingQueue<Integer> queue1 = new PriorityBlockingQueue<>(); // Constructor with initial capacity BlockingQueue<Integer> queue2 = new PriorityBlockingQueue<>(5); // Constructor with initial capacity and comparator Comparator<Integer> comparator = Integer::compareTo; BlockingQueue<Integer> queue3 = new PriorityBlockingQueue<>(5, comparator); // Constructor with initial collection of elements Collection<Integer> initialElements = java.util.Arrays.asList(1, 3, 2); BlockingQueue<Integer> queue4 = new PriorityBlockingQueue<>(initialElements)
DelayQueue: Een vertraagde wachtrij waarbij items pas kunnen worden opgehaald nadat hun vertraging is verlopen.
DelayedElement.java
DelayQueueConstructors.java
123456789101112131415161718class DelayedElement implements Delayed { private final long expirationTime; // The time when the element will be available public DelayedElement(long delay, TimeUnit unit) { this.expirationTime = System.currentTimeMillis() + unit.toMillis(delay); } @Override public long getDelay(TimeUnit unit) { long delay = expirationTime - System.currentTimeMillis(); // Calculate the remaining delay return unit.convert(delay, TimeUnit.MILLISECONDS); // Convert the delay to the specified time unit } @Override public int compareTo(Delayed o) { return Long.compare(this.expirationTime, ((DelayedElement) o).expirationTime); } }
Deze code toont het gebruik van de DelayedElement klasse, die implementeert het Delayed interface, en de DelayQueue wachtrij in Java. De DelayedElement klasse definieert een getDelay methode om de resterende vertragingstijd te berekenen en een compareTo methode om objecten te vergelijken op basis van de verloopdatum van de vertraging.
De main methode maakt twee wachtrijen aan: queue1, een lege wachtrij, en queue2, een wachtrij die is geïnitialiseerd met elementen die respectievelijk een vertraging van 5 en 1 seconde hebben.
De items in DelayQueueue worden beschikbaar voor ophalen nadat de opgegeven vertragingstijd is verstreken.
SynchronousQueueue: Een wachtrij zonder capaciteit, waarbij elke invoegbewerking moet wachten op de overeenkomstige extractiebewerking en omgekeerd.
Main.java
12345// Constructor without fair access BlockingQueue<String> queue1 = new SynchronousQueue<>(); // Constructor with fair access BlockingQueue<String> queue2 = new SynchronousQueue<>(true);
De belangrijkste methoden van de BlockingQueue:
Elementen toevoegen:
De void put(E e) methode voegt een item toe aan de wachtrij en blokkeert de thread als de wachtrij vol is. Als alternatief probeert de boolean offer(E e, long timeout, TimeUnit unit) methode een item toe te voegen aan de wachtrij, waarbij wordt gewacht gedurende de opgegeven tijd als de wachtrij vol is.
Main.java
1234567891011121314151617public class BlockingQueueExample { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<>(2); try { queue.put("Element 1"); // Insert the first element, no blocking. queue.put("Element 2"); // Insert the second element, no blocking. // Try to add the third element with a 2-second timeout. // Since the queue is full, it will wait for 2 seconds. boolean success = queue.offer("Element 3", 2, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } } }
Dit voorbeeld toont het invoegen van twee elementen in een BlockingQueue zonder blokkering, gevolgd door een poging om een derde element toe te voegen met een timeout van 2 seconden via de offer()-methode, die zal wachten als de wachtrij vol is.
Ophalen van elementen:
De methode E take() haalt een item uit de wachtrij en retourneert deze, waarbij de thread wordt geblokkeerd als de wachtrij leeg is. Als alternatief probeert de methode E poll(long timeout, TimeUnit unit) een item uit de wachtrij te halen, waarbij wordt gewacht gedurende de opgegeven tijd als de wachtrij leeg is.
Main.java
1234567891011121314151617181920212223public class BlockingQueueRetrievalExample { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<>(2); try { // Adding elements to the queue queue.put("Element 1"); queue.put("Element 2"); // Retrieve and remove the first element, no blocking since the queue is not empty String item1 = queue.take(); // Returns "Element 1" // Attempt to retrieve and remove the next element with a 2-second timeout String item2 = queue.poll(2, TimeUnit.SECONDS); // Returns "Element 2" // Attempt to retrieve an element when the queue is empty, this will block for 2 seconds String item3 = queue.poll(2, TimeUnit.SECONDS); // Returns `null` after timeout } catch (InterruptedException e) { e.printStackTrace(); } } }
Deze code voegt twee elementen toe aan een BlockingQueue, haalt en verwijdert het eerste element direct, probeert het volgende element op te halen met een timeout van 2 seconden, en probeert tenslotte een element op te halen uit een lege queue, wat resulteert in null na de timeout.
Controleren en verwijderen van elementen:
De boolean remove(Object o) methode verwijdert het gespecificeerde element uit de queue als het aanwezig is. Daarentegen controleert de boolean contains(Object o) methode of het gespecificeerde element aanwezig is in de queue zonder het te verwijderen.
Main.java
1234567891011121314151617181920212223242526public class BlockingQueueCheckRemoveExample { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<>(2); try { // Adding elements to the queue queue.put("Element 1"); queue.put("Element 2"); // Check if "Element 1" is in the queue, should return `true` boolean containsElement1 = queue.contains("Element 1"); // true // Remove "Element 1" from the queue, should return `true` boolean removedElement1 = queue.remove("Element 1"); // true // Check if "Element 1" is still in the queue, should return `false` boolean containsElement1AfterRemoval = queue.contains("Element 1"); // false // Try to remove an element that is not in the queue, should return `false` boolean removedElement3 = queue.remove("Element 3"); // false } catch (InterruptedException e) { e.printStackTrace(); } } }
Deze code voegt twee elementen toe aan een BlockingQueue, controleert op de aanwezigheid van "Element 1", verwijdert dit, controleert opnieuw om de verwijdering te bevestigen en probeert vervolgens een niet-bestaand element te verwijderen.
Controle van de status van de wachtrij:
De methode int size() retourneert het aantal elementen dat zich momenteel in de wachtrij bevindt. Om te bepalen of de wachtrij leeg is, kan de methode boolean isEmpty() worden gebruikt, waarmee wordt gecontroleerd of de wachtrij geen elementen bevat. Voor wachtrijen met een vaste capaciteit geeft de methode int remainingCapacity() het aantal resterende beschikbare plaatsen in de wachtrij aan.
Main.java
123456789101112131415161718192021222324252627282930public class BlockingQueueCapacityExample { public static void main(String[] args) { BlockingQueue<String> queue = new ArrayBlockingQueue<>(3); try { // Adding elements to the queue queue.put("Element 1"); queue.put("Element 2"); // Get the number of elements in the queue int currentSize = queue.size(); // 2 // Check if the queue is empty boolean isQueueEmpty = queue.isEmpty(); // false // Get the remaining capacity in the queue int remainingSpace = queue.remainingCapacity(); // 1 // Add another element to fill the queue queue.put("Element 3"); // Check the size and remaining capacity after adding the third element currentSize = queue.size(); // 3 remainingSpace = queue.remainingCapacity(); // 0 } catch (InterruptedException e) { e.printStackTrace(); } } }
Deze code voegt elementen toe aan een BlockingQueue, controleert de huidige grootte, verifieert of de wachtrij leeg is, en bepaalt de resterende capaciteit, waarna deze waarden worden bijgewerkt nadat de wachtrij volledig is gevuld.
Een praktijkvoorbeeld in code realiseren
😭 Beperkingen
Een belangrijke beperking is prestatie: door de betrokken vergrendelingsbewerkingen kan de prestatie lager zijn dan bij niet-gesynchroniseerde collecties. Daarnaast kunnen resources een probleem vormen, aangezien grote wachtrijen meer geheugen en CPU-tijd vereisen om de vergrendelingen en synchronisatieprocessen af te handelen.
💪 Voordelen
Aan de positieve kant is het systeem veilig bij multithreading, waardoor veilige communicatie tussen threads mogelijk is zonder handmatig synchronisatiebeheer. Het vereenvoudigt ook de code door complexe synchronisatie- en blokkeringsconstructies te vermijden. Bovendien zorgt de flexibiliteit van verschillende BlockingQueue-implementaties ervoor dat ze geschikt zijn voor diverse gebruiksscenario's.
1. Wat is een BlockingQueue in Java?
2. Wat zijn de belangrijkste methoden van BlockingQueue die een thread blokkeren?
3. Waarvoor is BlockingQueue nuttig in multithreaded applicaties?
Bedankt voor je feedback!