Semáforo y Barrera
En los programas multihilo, a menudo es necesario controlar el acceso a los recursos o sincronizar la ejecución de hilos. Semaphore y Barrier son mecanismos de sincronización de alto nivel que ayudan a abordar estos desafíos.
Hoy exploraremos cada uno de estos mecanismos en secuencia y comprenderemos sus diferencias. Comencemos con Semaphore.
Semaphore en Java se implementan a través de la clase java.util.concurrent.Semaphore.
Constructores
Semaphore(int permits): Constructor que crea un semaphore con un número determinado de permisos. Los permisos representan el número de accesos al recurso compartido.
Main.java
1Semaphore semaphore = new Semaphore(20);
Semaphore(int permits, boolean fair): Constructor que proporciona resolución por orden de llegada, primero en entrar, primero en ser atendido.
Main.java
1Semaphore semaphore = new Semaphore(20, true);
Si fair se establece en true, el semaphore otorgará permisos en orden primero en entrar, primero en salir (FIFO), lo que puede ayudar a evitar el hambre. Por defecto - false.
Métodos principales
El método acquire() solicita un permiso único. Si hay un permiso disponible, se otorga inmediatamente; de lo contrario, el hilo queda bloqueado hasta que un permiso esté disponible. Una vez que se completa una tarea, se utiliza el método release() para liberar el permiso, devolviéndolo al semaphore. Si otros hilos estaban esperando un permiso, uno de ellos será desbloqueado.
Imagine un estacionamiento con un número limitado de espacios. El semaphore funciona como un controlador, llevando el registro de los espacios disponibles y denegando el acceso una vez que el estacionamiento está lleno.
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(); } } }
También es posible consultar cuántos permisos están actualmente disponibles en Semaphore utilizando el método int availablePermits(). Además, se puede intentar obtener un permiso mediante el método boolean tryAcquire(), que devuelve true si se obtuvo un permiso y false en caso contrario.
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()); } } } }
En otras palabras, un Semaphore es útil cuando se necesita proporcionar acceso limitado y simultáneo a un segmento específico de código. La única desventaja es la posibilidad de un interbloqueo si los hilos quedan bloqueados en un orden incorrecto.
Ahora, pasemos al siguiente mecanismo de sincronización, que es aún más sencillo de utilizar pero será totalmente valioso para sus necesidades.
CyclicBarrier
Barrier en Java está representado por la clase java.util.concurrent.CyclicBarrier. Los principales métodos de CyclicBarrier incluyen:
Constructores de CyclicBarrier
CyclicBarrier(int parties): Constructor que crea una barrera que bloquea los hilos hasta que llega un cierto número de hilos (parties).
Main.java
1CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
CyclicBarrier(int parties, Runnable barrierAction): Constructor que crea una barrera con un número determinado de participantes y una acción (barrierAction) que se ejecuta cuando todos los participantes llegan a la barrera.
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);
Métodos de CyclicBarrier
El método principal await() que se utiliza como barrera y no permite que el hilo continúe hasta que todos los hilos alcancen este método. Devuelve un número de secuencia que indica el orden de llegada de los participantes.
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(); } } } }
Puede ocurrir que no todos los hilos lleguen a la barrera y el programa quede bloqueado. Para este propósito se utiliza el método int await(long timeout, TimeUnit unit), que es similar a await(), pero con tiempo de espera. Si el tiempo de espera expira antes de que lleguen todos los participantes, el método genera una excepción TimeoutException.
También es posible conocer el número de participantes requeridos para completar la barrera mediante int getParties() y su método similar int getNumberWaiting(), que devuelve el número de participantes que actualmente esperan en la barrera.
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()); } } }
También es posible verificar si la barrera ha sido destruida si uno de los hilos es interrumpido o si el tiempo de espera ha expirado utilizando el método boolean isBroken(). Si ha sido destruida, se puede utilizar el método void reset() que simplemente restaurará la barrera.
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(); }
Se debe tener en cuenta que algún flujo puede no llegar a la barrera debido a un error u otra causa, y entonces es evidente que la barrera no permitirá avanzar a aquellos flujos que actualmente están esperando en la barrera.
¡Gracias por tus comentarios!
Pregunte a AI
Pregunte a AI
Pregunte lo que quiera o pruebe una de las preguntas sugeridas para comenzar nuestra charla
Awesome!
Completion rate improved to 3.33
Semáforo y Barrera
Desliza para mostrar el menú
En los programas multihilo, a menudo es necesario controlar el acceso a los recursos o sincronizar la ejecución de hilos. Semaphore y Barrier son mecanismos de sincronización de alto nivel que ayudan a abordar estos desafíos.
Hoy exploraremos cada uno de estos mecanismos en secuencia y comprenderemos sus diferencias. Comencemos con Semaphore.
Semaphore en Java se implementan a través de la clase java.util.concurrent.Semaphore.
Constructores
Semaphore(int permits): Constructor que crea un semaphore con un número determinado de permisos. Los permisos representan el número de accesos al recurso compartido.
Main.java
1Semaphore semaphore = new Semaphore(20);
Semaphore(int permits, boolean fair): Constructor que proporciona resolución por orden de llegada, primero en entrar, primero en ser atendido.
Main.java
1Semaphore semaphore = new Semaphore(20, true);
Si fair se establece en true, el semaphore otorgará permisos en orden primero en entrar, primero en salir (FIFO), lo que puede ayudar a evitar el hambre. Por defecto - false.
Métodos principales
El método acquire() solicita un permiso único. Si hay un permiso disponible, se otorga inmediatamente; de lo contrario, el hilo queda bloqueado hasta que un permiso esté disponible. Una vez que se completa una tarea, se utiliza el método release() para liberar el permiso, devolviéndolo al semaphore. Si otros hilos estaban esperando un permiso, uno de ellos será desbloqueado.
Imagine un estacionamiento con un número limitado de espacios. El semaphore funciona como un controlador, llevando el registro de los espacios disponibles y denegando el acceso una vez que el estacionamiento está lleno.
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(); } } }
También es posible consultar cuántos permisos están actualmente disponibles en Semaphore utilizando el método int availablePermits(). Además, se puede intentar obtener un permiso mediante el método boolean tryAcquire(), que devuelve true si se obtuvo un permiso y false en caso contrario.
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()); } } } }
En otras palabras, un Semaphore es útil cuando se necesita proporcionar acceso limitado y simultáneo a un segmento específico de código. La única desventaja es la posibilidad de un interbloqueo si los hilos quedan bloqueados en un orden incorrecto.
Ahora, pasemos al siguiente mecanismo de sincronización, que es aún más sencillo de utilizar pero será totalmente valioso para sus necesidades.
CyclicBarrier
Barrier en Java está representado por la clase java.util.concurrent.CyclicBarrier. Los principales métodos de CyclicBarrier incluyen:
Constructores de CyclicBarrier
CyclicBarrier(int parties): Constructor que crea una barrera que bloquea los hilos hasta que llega un cierto número de hilos (parties).
Main.java
1CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
CyclicBarrier(int parties, Runnable barrierAction): Constructor que crea una barrera con un número determinado de participantes y una acción (barrierAction) que se ejecuta cuando todos los participantes llegan a la barrera.
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);
Métodos de CyclicBarrier
El método principal await() que se utiliza como barrera y no permite que el hilo continúe hasta que todos los hilos alcancen este método. Devuelve un número de secuencia que indica el orden de llegada de los participantes.
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(); } } } }
Puede ocurrir que no todos los hilos lleguen a la barrera y el programa quede bloqueado. Para este propósito se utiliza el método int await(long timeout, TimeUnit unit), que es similar a await(), pero con tiempo de espera. Si el tiempo de espera expira antes de que lleguen todos los participantes, el método genera una excepción TimeoutException.
También es posible conocer el número de participantes requeridos para completar la barrera mediante int getParties() y su método similar int getNumberWaiting(), que devuelve el número de participantes que actualmente esperan en la barrera.
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()); } } }
También es posible verificar si la barrera ha sido destruida si uno de los hilos es interrumpido o si el tiempo de espera ha expirado utilizando el método boolean isBroken(). Si ha sido destruida, se puede utilizar el método void reset() que simplemente restaurará la barrera.
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(); }
Se debe tener en cuenta que algún flujo puede no llegar a la barrera debido a un error u otra causa, y entonces es evidente que la barrera no permitirá avanzar a aquellos flujos que actualmente están esperando en la barrera.
¡Gracias por tus comentarios!