Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Leer Blockingqueue en de Implementaties Ervan | Gesynchroniseerde Collecties
Multithreading in Java

bookBlockingqueue 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.

Note
Opmerking

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

Main.java

copy
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);
Note
Opmerking

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

Main.java

copy
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

Main.java

copy
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

DelayedElement.java

DelayQueueConstructors.java

DelayQueueConstructors.java

copy
123456789101112131415161718
class 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

Main.java

copy
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

Main.java

copy
1234567891011121314151617
public 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

Main.java

copy
1234567891011121314151617181920212223
public 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

Main.java

copy
1234567891011121314151617181920212223242526
public 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

Main.java

copy
123456789101112131415161718192021222324252627282930
public 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?

question mark

Wat is een BlockingQueue in Java?

Select the correct answer

question mark

Wat zijn de belangrijkste methoden van BlockingQueue die een thread blokkeren?

Select the correct answer

question mark

Waarvoor is BlockingQueue nuttig in multithreaded applicaties?

Select the correct answer

Was alles duidelijk?

Hoe kunnen we het verbeteren?

Bedankt voor je feedback!

Sectie 2. Hoofdstuk 3

Vraag AI

expand

Vraag AI

ChatGPT

Vraag wat u wilt of probeer een van de voorgestelde vragen om onze chat te starten.

Suggested prompts:

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

bookBlockingqueue 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.

Note
Opmerking

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

Main.java

copy
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);
Note
Opmerking

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

Main.java

copy
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

Main.java

copy
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

DelayedElement.java

DelayQueueConstructors.java

DelayQueueConstructors.java

copy
123456789101112131415161718
class 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

Main.java

copy
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

Main.java

copy
1234567891011121314151617
public 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

Main.java

copy
1234567891011121314151617181920212223
public 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

Main.java

copy
1234567891011121314151617181920212223242526
public 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

Main.java

copy
123456789101112131415161718192021222324252627282930
public 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?

question mark

Wat is een BlockingQueue in Java?

Select the correct answer

question mark

Wat zijn de belangrijkste methoden van BlockingQueue die een thread blokkeren?

Select the correct answer

question mark

Waarvoor is BlockingQueue nuttig in multithreaded applicaties?

Select the correct answer

Was alles duidelijk?

Hoe kunnen we het verbeteren?

Bedankt voor je feedback!

Sectie 2. Hoofdstuk 3
some-alt