Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Lære Semafor og Barriere | Højniveausynkroniseringsmekanismer
Multitrådning i Java

bookSemafor og Barriere

I multitrådede programmer er det ofte nødvendigt at kontrollere adgangen til ressourcer eller synkronisere trådenes udførelse. Semaphore og Barrier er højniveau synkroniseringsmekanismer, der hjælper med at håndtere disse udfordringer.

I dag vil vi undersøge hver af disse mekanismer i rækkefølge og forstå deres forskelle. Lad os begynde med Semaphore.

Semaphore i Java implementeres gennem klassen java.util.concurrent.Semaphore.

Konstruktører

Semaphore(int permits): Konstruktør, der opretter en semaphore med et bestemt antal tilladelser. Tilladelserne repræsenterer antallet af adgange til den delte ressource.

Main.java

Main.java

copy
1
Semaphore semaphore = new Semaphore(20);

Semaphore(int permits, boolean fair): Konstruktør, der sikrer først-til-mølle-princippet.

Main.java

Main.java

copy
1
Semaphore semaphore = new Semaphore(20, true);

Hvis fair er sat til true, vil semaphore tildele tilladelser i først-ind-først-ud (FIFO) rækkefølge, hvilket kan hjælpe med at undgå sult. Standard - false.

Hovedmetoder

Metoden acquire() anmoder om en enkelt tilladelse. Hvis en tilladelse er tilgængelig, tildeles den straks; ellers bliver tråden blokeret, indtil en tilladelse bliver tilgængelig. Når en opgave er fuldført, bruges metoden release() til at frigive tilladelsen og returnere den til semaphore. Hvis andre tråde ventede på en tilladelse, vil én af dem blive afblokeret.

Forestil dig en parkeringsplads med et begrænset antal pladser. semaphore fungerer som en kontrolinstans, der holder styr på de tilgængelige pladser og afviser adgang, når pladsen er fuld.

Main.java

Main.java

copy
1234567891011121314151617181920212223242526272829303132
package 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(); } } }

Du kan også finde ud af, hvor mange tilladelser der i øjeblikket er tilgængelige i Semaphore ved hjælp af metoden int availablePermits(). Du kan også forsøge at få en tilladelse ved hjælp af metoden boolean tryAcquire(), som returnerer true, hvis en tilladelse blev opnået, og false, hvis ikke.

Main.java

Main.java

copy
1234567891011121314151617181920212223242526272829303132333435363738394041
package 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()); } } } }
Note
Bemærk

Med andre ord er en Semaphore nyttig, når der skal gives begrænset samtidig adgang til et bestemt kodeafsnit. Den eneste ulempe er risikoen for deadlock, hvis tråde blokeres i forkert rækkefølge.

Lad os nu gå videre til den næste synchroniseringsmekanisme, som er endnu nemmere at anvende, men vil være 100 procent værdifuld for dine behov.

CyclicBarrier

Barrier i Java repræsenteres af klassen java.util.concurrent.CyclicBarrier. De vigtigste metoder i CyclicBarrier omfatter:

Konstruktører CyclicBarrier

CyclicBarrier(int parties): Konstruktør, der opretter en barrier, som blokerer tråde, indtil et bestemt antal tråde (parties) ankommer.

Main.java

Main.java

copy
1
CyclicBarrier cyclicBarrier = new CyclicBarrier(10);

CyclicBarrier(int parties, Runnable barrierAction): Konstruktør, der opretter en barriere med et angivet antal parter og en handling (barrierAction), som udføres, når alle parter ankommer til barrieren.

Main.java

Main.java

copy
1234567
Runnable 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);

Metoder CyclicBarrier

Hovedmetoden await(), som bruges som en barriere og ikke lader tråden fortsætte, før alle tråde når denne metode. Returnerer et sekvens-nummer, der angiver rækkefølgen for deltagernes ankomst.

Main.java

Main.java

copy
1234567891011121314151617181920212223242526272829303132333435363738394041424344
package 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(); } } } }

Det kan ske, at ikke alle tråde vil nå barrieren, og programmet vil gå i stå. Til dette formål anvendes metoden int await(long timeout, TimeUnit unit), som ligner await(), men med timeout. Hvis timeout udløber, før alle deltagere ankommer, genererer metoden en TimeoutException-undtagelse.

Det er også muligt at finde ud af antallet af deltagere, der kræves for at fuldføre barrieren med int getParties() samt den lignende metode int getNumberWaiting(), som returnerer antallet af deltagere, der aktuelt venter ved barrieren.

Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
package 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()); } } }

Det er også muligt at kontrollere, om barrieren er blevet ødelagt, hvis en af trådene bliver afbrudt, eller hvis ventetiden er udløbet, ved at bruge boolean isBroken() metoden. Hvis den er blevet ødelagt, kan du bruge void reset() metoden, som simpelt gendanner barrieren.

Main.java

Main.java

copy
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(); }
Note
Bemærk

Det skal tages i betragtning, at nogle tråde muligvis ikke når barrieren på grund af en fejl eller andet, og det er derfor tydeligt, at barrieren ikke vil tillade de tråde, der i øjeblikket venter ved barrieren, at fortsætte.

Var alt klart?

Hvordan kan vi forbedre det?

Tak for dine kommentarer!

Sektion 3. Kapitel 3

Spørg AI

expand

Spørg AI

ChatGPT

Spørg om hvad som helst eller prøv et af de foreslåede spørgsmål for at starte vores chat

Suggested prompts:

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

bookSemafor og Barriere

Stryg for at vise menuen

I multitrådede programmer er det ofte nødvendigt at kontrollere adgangen til ressourcer eller synkronisere trådenes udførelse. Semaphore og Barrier er højniveau synkroniseringsmekanismer, der hjælper med at håndtere disse udfordringer.

I dag vil vi undersøge hver af disse mekanismer i rækkefølge og forstå deres forskelle. Lad os begynde med Semaphore.

Semaphore i Java implementeres gennem klassen java.util.concurrent.Semaphore.

Konstruktører

Semaphore(int permits): Konstruktør, der opretter en semaphore med et bestemt antal tilladelser. Tilladelserne repræsenterer antallet af adgange til den delte ressource.

Main.java

Main.java

copy
1
Semaphore semaphore = new Semaphore(20);

Semaphore(int permits, boolean fair): Konstruktør, der sikrer først-til-mølle-princippet.

Main.java

Main.java

copy
1
Semaphore semaphore = new Semaphore(20, true);

Hvis fair er sat til true, vil semaphore tildele tilladelser i først-ind-først-ud (FIFO) rækkefølge, hvilket kan hjælpe med at undgå sult. Standard - false.

Hovedmetoder

Metoden acquire() anmoder om en enkelt tilladelse. Hvis en tilladelse er tilgængelig, tildeles den straks; ellers bliver tråden blokeret, indtil en tilladelse bliver tilgængelig. Når en opgave er fuldført, bruges metoden release() til at frigive tilladelsen og returnere den til semaphore. Hvis andre tråde ventede på en tilladelse, vil én af dem blive afblokeret.

Forestil dig en parkeringsplads med et begrænset antal pladser. semaphore fungerer som en kontrolinstans, der holder styr på de tilgængelige pladser og afviser adgang, når pladsen er fuld.

Main.java

Main.java

copy
1234567891011121314151617181920212223242526272829303132
package 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(); } } }

Du kan også finde ud af, hvor mange tilladelser der i øjeblikket er tilgængelige i Semaphore ved hjælp af metoden int availablePermits(). Du kan også forsøge at få en tilladelse ved hjælp af metoden boolean tryAcquire(), som returnerer true, hvis en tilladelse blev opnået, og false, hvis ikke.

Main.java

Main.java

copy
1234567891011121314151617181920212223242526272829303132333435363738394041
package 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()); } } } }
Note
Bemærk

Med andre ord er en Semaphore nyttig, når der skal gives begrænset samtidig adgang til et bestemt kodeafsnit. Den eneste ulempe er risikoen for deadlock, hvis tråde blokeres i forkert rækkefølge.

Lad os nu gå videre til den næste synchroniseringsmekanisme, som er endnu nemmere at anvende, men vil være 100 procent værdifuld for dine behov.

CyclicBarrier

Barrier i Java repræsenteres af klassen java.util.concurrent.CyclicBarrier. De vigtigste metoder i CyclicBarrier omfatter:

Konstruktører CyclicBarrier

CyclicBarrier(int parties): Konstruktør, der opretter en barrier, som blokerer tråde, indtil et bestemt antal tråde (parties) ankommer.

Main.java

Main.java

copy
1
CyclicBarrier cyclicBarrier = new CyclicBarrier(10);

CyclicBarrier(int parties, Runnable barrierAction): Konstruktør, der opretter en barriere med et angivet antal parter og en handling (barrierAction), som udføres, når alle parter ankommer til barrieren.

Main.java

Main.java

copy
1234567
Runnable 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);

Metoder CyclicBarrier

Hovedmetoden await(), som bruges som en barriere og ikke lader tråden fortsætte, før alle tråde når denne metode. Returnerer et sekvens-nummer, der angiver rækkefølgen for deltagernes ankomst.

Main.java

Main.java

copy
1234567891011121314151617181920212223242526272829303132333435363738394041424344
package 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(); } } } }

Det kan ske, at ikke alle tråde vil nå barrieren, og programmet vil gå i stå. Til dette formål anvendes metoden int await(long timeout, TimeUnit unit), som ligner await(), men med timeout. Hvis timeout udløber, før alle deltagere ankommer, genererer metoden en TimeoutException-undtagelse.

Det er også muligt at finde ud af antallet af deltagere, der kræves for at fuldføre barrieren med int getParties() samt den lignende metode int getNumberWaiting(), som returnerer antallet af deltagere, der aktuelt venter ved barrieren.

Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
package 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()); } } }

Det er også muligt at kontrollere, om barrieren er blevet ødelagt, hvis en af trådene bliver afbrudt, eller hvis ventetiden er udløbet, ved at bruge boolean isBroken() metoden. Hvis den er blevet ødelagt, kan du bruge void reset() metoden, som simpelt gendanner barrieren.

Main.java

Main.java

copy
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(); }
Note
Bemærk

Det skal tages i betragtning, at nogle tråde muligvis ikke når barrieren på grund af en fejl eller andet, og det er derfor tydeligt, at barrieren ikke vil tillade de tråde, der i øjeblikket venter ved barrieren, at fortsætte.

Var alt klart?

Hvordan kan vi forbedre det?

Tak for dine kommentarer!

Sektion 3. Kapitel 3
some-alt