Semaphore en Barrière
In multithreaded programma's is het vaak noodzakelijk om de toegang tot resources te beheren of de uitvoering van threads te synchroniseren. Semaphore en Barrier zijn high-level synchronisatiemechanismen die helpen bij het aanpakken van deze uitdagingen.
Vandaag verkennen we elk van deze mechanismen achtereenvolgens en begrijpen we hun verschillen. We beginnen met Semaphore.
Semaphore in Java worden geïmplementeerd via de klasse java.util.concurrent.Semaphore.
Constructors
Semaphore(int permits): Constructor die een semaphore aanmaakt met een bepaald aantal permissies. De permissies vertegenwoordigen het aantal toegangen tot de gedeelde resource.
Main.java
1Semaphore semaphore = new Semaphore(20);
Semaphore(int permits, boolean fair): Constructor die wie het eerst komt, het eerst maalt-oplossing biedt.
Main.java
1Semaphore semaphore = new Semaphore(20, true);
Als fair is ingesteld op true, zal de semaphore permissies toekennen in first-in-first-out (FIFO) volgorde, wat kan helpen om uithongering te voorkomen. Standaard - false.
Belangrijkste methoden
De methode acquire() vraagt een enkele permissie aan. Als er een permissie beschikbaar is, wordt deze onmiddellijk toegekend; anders wordt de thread geblokkeerd totdat een permissie beschikbaar komt. Zodra een taak is voltooid, wordt de methode release() gebruikt om de permissie vrij te geven, zodat deze terugkeert naar de semaphore. Als andere threads wachtten op een permissie, wordt één van hen gedeblokkeerd.
Stel je een parkeerterrein voor met een beperkt aantal plaatsen. De semaphore fungeert als een controller die het aantal beschikbare plaatsen bijhoudt en toegang weigert zodra het terrein vol is.
Main.java
1234567891011121314151617181920212223242526272829303132package com.example; import java.util.concurrent.Semaphore; public class Main { private final Semaphore semaphore; public Main(int slots) { semaphore = new Semaphore(slots); } public void parkCar() { try { semaphore.acquire(); // Request a parking spot System.out.println("Car parked. Available slots: " + semaphore.availablePermits()); Thread.sleep(2000); // Simulate parking time } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { semaphore.release(); // Release the parking spot System.out.println("Car left. Available slots: " + semaphore.availablePermits()); } } public static void main(String[] args) { Main parking = new Main(3); // Parking lot with 3 spots for (int i = 0; i < 5; i++) { new Thread(parking::parkCar).start(); } } }
Het is ook mogelijk om te achterhalen hoeveel toestemmingen momenteel beschikbaar zijn in Semaphore met de methode int availablePermits(). Daarnaast kan geprobeerd worden om een toestemming te verkrijgen met de methode boolean tryAcquire(), die true retourneert als een toestemming is verkregen en false als dat niet het geval is.
Main.java
1234567891011121314151617181920212223242526272829303132333435363738394041package com.example; import java.util.concurrent.Semaphore; public class Main { // Define the maximum number of permits available private static final int MAX_PERMITS = 3; private static Semaphore semaphore = new Semaphore(MAX_PERMITS); public static void main(String[] args) { // Create and start 5 worker threads for (int i = 1; i <= 5; i++) { new Thread(new Worker(), "Worker-" + i).start(); } } static class Worker implements Runnable { @Override public void run() { String name = Thread.currentThread().getName(); System.out.println(name + " trying to acquire a permit..."); // Try to acquire a permit if (semaphore.tryAcquire()) { try { System.out.println(name + " acquired a permit! Available permits: " + semaphore.availablePermits()); Thread.sleep(1000); // Simulate work } catch (InterruptedException e) { e.printStackTrace(); } finally { // Release the permit after the work is done semaphore.release(); System.out.println(name + " released a permit. Available permits: " + semaphore.availablePermits()); } } else { // Inform if the permit could not be acquired System.out.println(name + " could not acquire a permit. Available permits: " + semaphore.availablePermits()); } } } }
Met andere woorden, een Semaphore is nuttig wanneer beperkte gelijktijdige toegang tot een specifiek codegedeelte vereist is. Het enige nadeel is de mogelijkheid van een deadlock als threads in de verkeerde volgorde worden geblokkeerd.
Laten we nu verder gaan met het volgende synchronisatiemechanisme, dat nog eenvoudiger te gebruiken is maar volledig waardevol zal zijn voor uw behoeften.
CyclicBarrier
Barrier in Java wordt weergegeven door de klasse java.util.concurrent.CyclicBarrier. De belangrijkste methoden van CyclicBarrier zijn onder andere:
Constructors CyclicBarrier
CyclicBarrier(int parties): Constructor die een barrier aanmaakt die threads blokkeert totdat een bepaald aantal threads (parties) is gearriveerd.
Main.java
1CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
CyclicBarrier(int parties, Runnable barrierAction): Constructor die een barrière creëert met een opgegeven aantal partijen en een actie (barrierAction) die wordt uitgevoerd wanneer alle partijen de barrière bereiken.
Main.java
1234567Runnable task = () -> { // This task will be executed when all parties have reached the barrier System.out.println("Hello))"); }; // Create a `CyclicBarrier` for 10 parties with a barrier action CyclicBarrier cyclicBarrier = new CyclicBarrier(10, task);
Methoden CyclicBarrier
De belangrijkste methode await() die wordt gebruikt als een barrière en de thread niet verder laat gaan totdat alle threads deze methode bereiken. Geeft een volgnummer nummer terug dat de volgorde van aankomst van de deelnemers aangeeft.
Main.java
1234567891011121314151617181920212223242526272829303132333435363738394041424344package com.example; import java.util.concurrent.CyclicBarrier; public class Main { public static void main(String[] args) { // Create a `CyclicBarrier` for 3 parties with a barrier action CyclicBarrier barrier = new CyclicBarrier(3, () -> { System.out.println("All parties have reached the barrier. Barrier action executed."); }); // Create and start 3 worker threads for (int i = 1; i <= 3; i++) { new Thread(new Worker(barrier), "Worker-" + i).start(); } } static class Worker implements Runnable { private CyclicBarrier barrier; Worker(CyclicBarrier barrier) { this.barrier = barrier; } @Override public void run() { String name = Thread.currentThread().getName(); System.out.println(name + " is working..."); try { // Simulate work Thread.sleep((int) (Math.random() * 1000)); System.out.println(name + " is waiting at the barrier."); barrier.await(); // Wait at the barrier // This code will execute after all parties have reached the barrier System.out.println(name + " has crossed the barrier."); } catch (Exception e) { e.printStackTrace(); } } } }
Het kan voorkomen dat niet alle threads de barrière bereiken en het programma vastloopt. Hiervoor wordt de methode await(long timeout, TimeUnit unit) gebruikt, die vergelijkbaar is met await(), maar met een timeout. Als de timeout verloopt voordat alle deelnemers arriveren, genereert de methode een TimeoutException-uitzondering.
Het is ook mogelijk om het aantal deelnemers dat nodig is om de barrière te voltooien te achterhalen met int getParties() en de vergelijkbare methode int getNumberWaiting(), die het aantal deelnemers dat momenteel bij de barrière wacht retourneert.
Main.java
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849package com.example; import java.util.concurrent.CyclicBarrier; public class Main { public static void main(String[] args) { // Create a `CyclicBarrier` for 3 parties with a barrier action CyclicBarrier barrier = new CyclicBarrier(3, () -> { System.out.println("All parties have reached the barrier. Barrier action executed."); }); System.out.println("Total number of parties required to complete the barrier: " + barrier.getParties()); // Create and start 3 worker threads for (int i = 1; i <= 3; i++) { new Thread(new Worker(barrier), "Worker-" + i).start(); } } static class Worker implements Runnable { private CyclicBarrier barrier; Worker(CyclicBarrier barrier) { this.barrier = barrier; } @Override public void run() { String name = Thread.currentThread().getName(); System.out.println(name + " is working..."); try { // Simulate work Thread.sleep((int) (Math.random() * 1000)); System.out.println(name + " is waiting at the barrier."); barrier.await(); // Wait at the barrier // This code will execute after all parties have reached the barrier System.out.println(name + " has crossed the barrier."); } catch (Exception e) { e.printStackTrace(); } // Print the number of participants currently waiting at the barrier System.out.println("Number of participants currently waiting at the barrier: " + barrier.getNumberWaiting()); } } }
Het is ook mogelijk om te controleren of de barrière is vernietigd wanneer een van de threads wordt onderbroken of de wachttijd is verlopen, met behulp van de boolean isBroken() methode. Indien deze is verbroken, kan de void reset() methode worden gebruikt, waarmee de barrière eenvoudig wordt hersteld.
Main.java
12345// Check if the barrier is broken and reset it if necessary if (barrier.isBroken()) { System.out.println("Barrier is broken. Resetting the barrier."); barrier.reset(); }
Er moet rekening mee worden gehouden dat sommige threads mogelijk de barrière niet bereiken vanwege een fout of iets anders, en dan is het duidelijk dat de barrière de threads die momenteel bij de barrière wachten, niet zal doorlaten.
Bedankt voor je feedback!
Vraag AI
Vraag AI
Vraag wat u wilt of probeer een van de voorgestelde vragen om onze chat te starten.
Awesome!
Completion rate improved to 3.33
Semaphore en Barrière
Veeg om het menu te tonen
In multithreaded programma's is het vaak noodzakelijk om de toegang tot resources te beheren of de uitvoering van threads te synchroniseren. Semaphore en Barrier zijn high-level synchronisatiemechanismen die helpen bij het aanpakken van deze uitdagingen.
Vandaag verkennen we elk van deze mechanismen achtereenvolgens en begrijpen we hun verschillen. We beginnen met Semaphore.
Semaphore in Java worden geïmplementeerd via de klasse java.util.concurrent.Semaphore.
Constructors
Semaphore(int permits): Constructor die een semaphore aanmaakt met een bepaald aantal permissies. De permissies vertegenwoordigen het aantal toegangen tot de gedeelde resource.
Main.java
1Semaphore semaphore = new Semaphore(20);
Semaphore(int permits, boolean fair): Constructor die wie het eerst komt, het eerst maalt-oplossing biedt.
Main.java
1Semaphore semaphore = new Semaphore(20, true);
Als fair is ingesteld op true, zal de semaphore permissies toekennen in first-in-first-out (FIFO) volgorde, wat kan helpen om uithongering te voorkomen. Standaard - false.
Belangrijkste methoden
De methode acquire() vraagt een enkele permissie aan. Als er een permissie beschikbaar is, wordt deze onmiddellijk toegekend; anders wordt de thread geblokkeerd totdat een permissie beschikbaar komt. Zodra een taak is voltooid, wordt de methode release() gebruikt om de permissie vrij te geven, zodat deze terugkeert naar de semaphore. Als andere threads wachtten op een permissie, wordt één van hen gedeblokkeerd.
Stel je een parkeerterrein voor met een beperkt aantal plaatsen. De semaphore fungeert als een controller die het aantal beschikbare plaatsen bijhoudt en toegang weigert zodra het terrein vol is.
Main.java
1234567891011121314151617181920212223242526272829303132package com.example; import java.util.concurrent.Semaphore; public class Main { private final Semaphore semaphore; public Main(int slots) { semaphore = new Semaphore(slots); } public void parkCar() { try { semaphore.acquire(); // Request a parking spot System.out.println("Car parked. Available slots: " + semaphore.availablePermits()); Thread.sleep(2000); // Simulate parking time } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { semaphore.release(); // Release the parking spot System.out.println("Car left. Available slots: " + semaphore.availablePermits()); } } public static void main(String[] args) { Main parking = new Main(3); // Parking lot with 3 spots for (int i = 0; i < 5; i++) { new Thread(parking::parkCar).start(); } } }
Het is ook mogelijk om te achterhalen hoeveel toestemmingen momenteel beschikbaar zijn in Semaphore met de methode int availablePermits(). Daarnaast kan geprobeerd worden om een toestemming te verkrijgen met de methode boolean tryAcquire(), die true retourneert als een toestemming is verkregen en false als dat niet het geval is.
Main.java
1234567891011121314151617181920212223242526272829303132333435363738394041package com.example; import java.util.concurrent.Semaphore; public class Main { // Define the maximum number of permits available private static final int MAX_PERMITS = 3; private static Semaphore semaphore = new Semaphore(MAX_PERMITS); public static void main(String[] args) { // Create and start 5 worker threads for (int i = 1; i <= 5; i++) { new Thread(new Worker(), "Worker-" + i).start(); } } static class Worker implements Runnable { @Override public void run() { String name = Thread.currentThread().getName(); System.out.println(name + " trying to acquire a permit..."); // Try to acquire a permit if (semaphore.tryAcquire()) { try { System.out.println(name + " acquired a permit! Available permits: " + semaphore.availablePermits()); Thread.sleep(1000); // Simulate work } catch (InterruptedException e) { e.printStackTrace(); } finally { // Release the permit after the work is done semaphore.release(); System.out.println(name + " released a permit. Available permits: " + semaphore.availablePermits()); } } else { // Inform if the permit could not be acquired System.out.println(name + " could not acquire a permit. Available permits: " + semaphore.availablePermits()); } } } }
Met andere woorden, een Semaphore is nuttig wanneer beperkte gelijktijdige toegang tot een specifiek codegedeelte vereist is. Het enige nadeel is de mogelijkheid van een deadlock als threads in de verkeerde volgorde worden geblokkeerd.
Laten we nu verder gaan met het volgende synchronisatiemechanisme, dat nog eenvoudiger te gebruiken is maar volledig waardevol zal zijn voor uw behoeften.
CyclicBarrier
Barrier in Java wordt weergegeven door de klasse java.util.concurrent.CyclicBarrier. De belangrijkste methoden van CyclicBarrier zijn onder andere:
Constructors CyclicBarrier
CyclicBarrier(int parties): Constructor die een barrier aanmaakt die threads blokkeert totdat een bepaald aantal threads (parties) is gearriveerd.
Main.java
1CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
CyclicBarrier(int parties, Runnable barrierAction): Constructor die een barrière creëert met een opgegeven aantal partijen en een actie (barrierAction) die wordt uitgevoerd wanneer alle partijen de barrière bereiken.
Main.java
1234567Runnable task = () -> { // This task will be executed when all parties have reached the barrier System.out.println("Hello))"); }; // Create a `CyclicBarrier` for 10 parties with a barrier action CyclicBarrier cyclicBarrier = new CyclicBarrier(10, task);
Methoden CyclicBarrier
De belangrijkste methode await() die wordt gebruikt als een barrière en de thread niet verder laat gaan totdat alle threads deze methode bereiken. Geeft een volgnummer nummer terug dat de volgorde van aankomst van de deelnemers aangeeft.
Main.java
1234567891011121314151617181920212223242526272829303132333435363738394041424344package com.example; import java.util.concurrent.CyclicBarrier; public class Main { public static void main(String[] args) { // Create a `CyclicBarrier` for 3 parties with a barrier action CyclicBarrier barrier = new CyclicBarrier(3, () -> { System.out.println("All parties have reached the barrier. Barrier action executed."); }); // Create and start 3 worker threads for (int i = 1; i <= 3; i++) { new Thread(new Worker(barrier), "Worker-" + i).start(); } } static class Worker implements Runnable { private CyclicBarrier barrier; Worker(CyclicBarrier barrier) { this.barrier = barrier; } @Override public void run() { String name = Thread.currentThread().getName(); System.out.println(name + " is working..."); try { // Simulate work Thread.sleep((int) (Math.random() * 1000)); System.out.println(name + " is waiting at the barrier."); barrier.await(); // Wait at the barrier // This code will execute after all parties have reached the barrier System.out.println(name + " has crossed the barrier."); } catch (Exception e) { e.printStackTrace(); } } } }
Het kan voorkomen dat niet alle threads de barrière bereiken en het programma vastloopt. Hiervoor wordt de methode await(long timeout, TimeUnit unit) gebruikt, die vergelijkbaar is met await(), maar met een timeout. Als de timeout verloopt voordat alle deelnemers arriveren, genereert de methode een TimeoutException-uitzondering.
Het is ook mogelijk om het aantal deelnemers dat nodig is om de barrière te voltooien te achterhalen met int getParties() en de vergelijkbare methode int getNumberWaiting(), die het aantal deelnemers dat momenteel bij de barrière wacht retourneert.
Main.java
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849package com.example; import java.util.concurrent.CyclicBarrier; public class Main { public static void main(String[] args) { // Create a `CyclicBarrier` for 3 parties with a barrier action CyclicBarrier barrier = new CyclicBarrier(3, () -> { System.out.println("All parties have reached the barrier. Barrier action executed."); }); System.out.println("Total number of parties required to complete the barrier: " + barrier.getParties()); // Create and start 3 worker threads for (int i = 1; i <= 3; i++) { new Thread(new Worker(barrier), "Worker-" + i).start(); } } static class Worker implements Runnable { private CyclicBarrier barrier; Worker(CyclicBarrier barrier) { this.barrier = barrier; } @Override public void run() { String name = Thread.currentThread().getName(); System.out.println(name + " is working..."); try { // Simulate work Thread.sleep((int) (Math.random() * 1000)); System.out.println(name + " is waiting at the barrier."); barrier.await(); // Wait at the barrier // This code will execute after all parties have reached the barrier System.out.println(name + " has crossed the barrier."); } catch (Exception e) { e.printStackTrace(); } // Print the number of participants currently waiting at the barrier System.out.println("Number of participants currently waiting at the barrier: " + barrier.getNumberWaiting()); } } }
Het is ook mogelijk om te controleren of de barrière is vernietigd wanneer een van de threads wordt onderbroken of de wachttijd is verlopen, met behulp van de boolean isBroken() methode. Indien deze is verbroken, kan de void reset() methode worden gebruikt, waarmee de barrière eenvoudig wordt hersteld.
Main.java
12345// Check if the barrier is broken and reset it if necessary if (barrier.isBroken()) { System.out.println("Barrier is broken. Resetting the barrier."); barrier.reset(); }
Er moet rekening mee worden gehouden dat sommige threads mogelijk de barrière niet bereiken vanwege een fout of iets anders, en dan is het duidelijk dat de barrière de threads die momenteel bij de barrière wachten, niet zal doorlaten.
Bedankt voor je feedback!