Semaphore und Barrier
In multithreaded Programmen ist es häufig notwendig, den Zugriff auf Ressourcen zu steuern oder die Ausführung von Threads zu synchronisieren. Semaphore und Barrier sind hochrangige Synchronisationsmechanismen, die helfen, diese Herausforderungen zu bewältigen.
Heute betrachten wir diese Mechanismen nacheinander und verstehen ihre Unterschiede. Beginnen wir mit dem Semaphore.
Semaphore in Java werden durch die Klasse java.util.concurrent.Semaphore implementiert.
Konstruktoren
Semaphore(int permits): Konstruktor, der ein semaphore mit einer bestimmten Anzahl von Berechtigungen erstellt. Die Berechtigungen repräsentieren die Anzahl der Zugriffe auf die gemeinsam genutzte Ressource.
Main.java
1Semaphore semaphore = new Semaphore(20);
Semaphore(int permits, boolean fair): Konstruktor, der eine First-Come, First-Served-Reihenfolge gewährleistet.
Main.java
1Semaphore semaphore = new Semaphore(20, true);
Wenn fair auf true gesetzt ist, vergibt das semaphore Berechtigungen in First-in-First-out-Reihenfolge (FIFO), was helfen kann, Verhungern zu vermeiden. Standardwert - false.
Hauptmethoden
Die Methode acquire() fordert eine einzelne Berechtigung an. Ist eine Berechtigung verfügbar, wird sie sofort gewährt; andernfalls wird der Thread blockiert, bis eine Berechtigung verfügbar wird. Nach Abschluss einer Aufgabe wird die Methode release() verwendet, um die Berechtigung freizugeben und an das semaphore zurückzugeben. Wenn andere Threads auf eine Berechtigung warten, wird einer von ihnen entblockt.
Stellen Sie sich einen Parkplatz mit einer begrenzten Anzahl von Stellplätzen vor. Das semaphore fungiert als Kontrollinstanz, die die verfügbaren Plätze überwacht und den Zugang verweigert, sobald der Parkplatz voll ist.
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(); } } }
Mit der Methode Semaphore kann die aktuelle Anzahl der Genehmigungen in einem int availablePermits() abgefragt werden. Mit der Methode boolean tryAcquire() kann versucht werden, eine Genehmigung zu erhalten; sie gibt true zurück, wenn eine Genehmigung erteilt wurde, und false, wenn nicht.
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()); } } } }
Mit anderen Worten: Ein Semaphore ist nützlich, wenn ein begrenzter gleichzeitiger Zugriff auf einen bestimmten Codeabschnitt ermöglicht werden soll. Der einzige Nachteil besteht in der Möglichkeit eines Deadlocks, falls Threads in der falschen Reihenfolge blockiert werden.
Als Nächstes folgt ein weiteres Synchronisationsmechanismus, der noch einfacher zu verwenden ist und für Ihre Anforderungen von hundertprozentigem Wert sein wird.
CyclicBarrier
Barrier in Java werden durch die Klasse java.util.concurrent.CyclicBarrier repräsentiert. Die wichtigsten Methoden von CyclicBarrier sind:
Konstruktoren CyclicBarrier
CyclicBarrier(int parties): Konstruktor, der eine Barriere erstellt, die Threads blockiert, bis eine bestimmte Anzahl von Threads (parties) eingetroffen ist.
Main.java
1CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
CyclicBarrier(int parties, Runnable barrierAction): Konstruktor, der eine Barriere mit einer angegebenen Anzahl von Parteien und einer Aktion (barrierAction) erstellt, die ausgeführt wird, wenn alle Parteien die Barriere erreicht haben.
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
Die Hauptmethode await(), die als Barriere dient und den Thread nicht weiterlaufen lässt, bis alle Threads diese Methode erreicht haben. Gibt eine Sequenz-Nummer zurück, die die Reihenfolge der Ankunft der Teilnehmer angibt.
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(); } } } }
Es kann vorkommen, dass nicht alle Threads die Barriere erreichen und das Programm hängen bleibt. Zu diesem Zweck wird die Methode await(long timeout, TimeUnit unit) verwendet, die ähnlich wie await() ist, jedoch mit Timeout. Wenn das Timeout abläuft, bevor alle Teilnehmer eingetroffen sind, löst die Methode eine TimeoutException aus.
Es ist auch möglich, die Anzahl der Teilnehmer zu ermitteln, die zum Abschluss der Barriere erforderlich sind, mit int getParties(), sowie die ähnliche Methode int getNumberWaiting(), die die Anzahl der aktuell an der Barriere wartenden Teilnehmer zurückgibt.
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()); } } }
Es ist ebenfalls möglich zu überprüfen, ob die Barriere zerstört wurde, wenn einer der Threads unterbrochen wird oder das Wartezeitlimit abgelaufen ist, indem die Methode boolean isBroken() verwendet wird. Falls sie zerstört wurde, kann die Methode void reset() genutzt werden, um die Barriere einfach wiederherzustellen.
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(); }
Es sollte berücksichtigt werden, dass einige Abläufe die Barriere möglicherweise aufgrund eines Fehlers oder anderer Umstände nicht erreichen, und in diesem Fall ist klar, dass die Barriere diejenigen Abläufe, die derzeit an der Barriere warten, nicht passieren lässt.
Danke für Ihr Feedback!
Fragen Sie AI
Fragen Sie AI
Fragen Sie alles oder probieren Sie eine der vorgeschlagenen Fragen, um unser Gespräch zu beginnen
Can you explain the main differences between Semaphore and CyclicBarrier?
How do I decide when to use Semaphore versus CyclicBarrier in my program?
Can you provide examples of real-world scenarios for each synchronization mechanism?
Awesome!
Completion rate improved to 3.33
Semaphore und Barrier
Swipe um das Menü anzuzeigen
In multithreaded Programmen ist es häufig notwendig, den Zugriff auf Ressourcen zu steuern oder die Ausführung von Threads zu synchronisieren. Semaphore und Barrier sind hochrangige Synchronisationsmechanismen, die helfen, diese Herausforderungen zu bewältigen.
Heute betrachten wir diese Mechanismen nacheinander und verstehen ihre Unterschiede. Beginnen wir mit dem Semaphore.
Semaphore in Java werden durch die Klasse java.util.concurrent.Semaphore implementiert.
Konstruktoren
Semaphore(int permits): Konstruktor, der ein semaphore mit einer bestimmten Anzahl von Berechtigungen erstellt. Die Berechtigungen repräsentieren die Anzahl der Zugriffe auf die gemeinsam genutzte Ressource.
Main.java
1Semaphore semaphore = new Semaphore(20);
Semaphore(int permits, boolean fair): Konstruktor, der eine First-Come, First-Served-Reihenfolge gewährleistet.
Main.java
1Semaphore semaphore = new Semaphore(20, true);
Wenn fair auf true gesetzt ist, vergibt das semaphore Berechtigungen in First-in-First-out-Reihenfolge (FIFO), was helfen kann, Verhungern zu vermeiden. Standardwert - false.
Hauptmethoden
Die Methode acquire() fordert eine einzelne Berechtigung an. Ist eine Berechtigung verfügbar, wird sie sofort gewährt; andernfalls wird der Thread blockiert, bis eine Berechtigung verfügbar wird. Nach Abschluss einer Aufgabe wird die Methode release() verwendet, um die Berechtigung freizugeben und an das semaphore zurückzugeben. Wenn andere Threads auf eine Berechtigung warten, wird einer von ihnen entblockt.
Stellen Sie sich einen Parkplatz mit einer begrenzten Anzahl von Stellplätzen vor. Das semaphore fungiert als Kontrollinstanz, die die verfügbaren Plätze überwacht und den Zugang verweigert, sobald der Parkplatz voll ist.
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(); } } }
Mit der Methode Semaphore kann die aktuelle Anzahl der Genehmigungen in einem int availablePermits() abgefragt werden. Mit der Methode boolean tryAcquire() kann versucht werden, eine Genehmigung zu erhalten; sie gibt true zurück, wenn eine Genehmigung erteilt wurde, und false, wenn nicht.
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()); } } } }
Mit anderen Worten: Ein Semaphore ist nützlich, wenn ein begrenzter gleichzeitiger Zugriff auf einen bestimmten Codeabschnitt ermöglicht werden soll. Der einzige Nachteil besteht in der Möglichkeit eines Deadlocks, falls Threads in der falschen Reihenfolge blockiert werden.
Als Nächstes folgt ein weiteres Synchronisationsmechanismus, der noch einfacher zu verwenden ist und für Ihre Anforderungen von hundertprozentigem Wert sein wird.
CyclicBarrier
Barrier in Java werden durch die Klasse java.util.concurrent.CyclicBarrier repräsentiert. Die wichtigsten Methoden von CyclicBarrier sind:
Konstruktoren CyclicBarrier
CyclicBarrier(int parties): Konstruktor, der eine Barriere erstellt, die Threads blockiert, bis eine bestimmte Anzahl von Threads (parties) eingetroffen ist.
Main.java
1CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
CyclicBarrier(int parties, Runnable barrierAction): Konstruktor, der eine Barriere mit einer angegebenen Anzahl von Parteien und einer Aktion (barrierAction) erstellt, die ausgeführt wird, wenn alle Parteien die Barriere erreicht haben.
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
Die Hauptmethode await(), die als Barriere dient und den Thread nicht weiterlaufen lässt, bis alle Threads diese Methode erreicht haben. Gibt eine Sequenz-Nummer zurück, die die Reihenfolge der Ankunft der Teilnehmer angibt.
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(); } } } }
Es kann vorkommen, dass nicht alle Threads die Barriere erreichen und das Programm hängen bleibt. Zu diesem Zweck wird die Methode await(long timeout, TimeUnit unit) verwendet, die ähnlich wie await() ist, jedoch mit Timeout. Wenn das Timeout abläuft, bevor alle Teilnehmer eingetroffen sind, löst die Methode eine TimeoutException aus.
Es ist auch möglich, die Anzahl der Teilnehmer zu ermitteln, die zum Abschluss der Barriere erforderlich sind, mit int getParties(), sowie die ähnliche Methode int getNumberWaiting(), die die Anzahl der aktuell an der Barriere wartenden Teilnehmer zurückgibt.
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()); } } }
Es ist ebenfalls möglich zu überprüfen, ob die Barriere zerstört wurde, wenn einer der Threads unterbrochen wird oder das Wartezeitlimit abgelaufen ist, indem die Methode boolean isBroken() verwendet wird. Falls sie zerstört wurde, kann die Methode void reset() genutzt werden, um die Barriere einfach wiederherzustellen.
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(); }
Es sollte berücksichtigt werden, dass einige Abläufe die Barriere möglicherweise aufgrund eines Fehlers oder anderer Umstände nicht erreichen, und in diesem Fall ist klar, dass die Barriere diejenigen Abläufe, die derzeit an der Barriere warten, nicht passieren lässt.
Danke für Ihr Feedback!