Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Aprende Semáforo y Barrera | Mecanismos de Sincronización de Alto Nivel
Multithreading en Java

bookSemá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

Main.java

copy
1
Semaphore 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

Main.java

copy
1
Semaphore 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

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

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

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
Nota

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

Main.java

copy
1
CyclicBarrier 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

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);

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

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

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

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

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

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
Nota

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.

¿Todo estuvo claro?

¿Cómo podemos mejorarlo?

¡Gracias por tus comentarios!

Sección 3. Capítulo 3

Pregunte a AI

expand

Pregunte a AI

ChatGPT

Pregunte lo que quiera o pruebe una de las preguntas sugeridas para comenzar nuestra charla

Awesome!

Completion rate improved to 3.33

bookSemá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

Main.java

copy
1
Semaphore 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

Main.java

copy
1
Semaphore 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

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

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

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
Nota

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

Main.java

copy
1
CyclicBarrier 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

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);

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

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

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

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

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

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
Nota

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.

¿Todo estuvo claro?

¿Cómo podemos mejorarlo?

¡Gracias por tus comentarios!

Sección 3. Capítulo 3
some-alt