Semáforo en Java

Un semáforo controla el acceso a un recurso compartido mediante el uso de un contador. Si el contador es mayor que cero, se permite el acceso. Si es cero, se deniega el acceso. Lo que está contando el contador son permisos que permiten el acceso al recurso compartido. Por lo tanto, para acceder al recurso, un subproceso debe recibir un permiso del semáforo.

Funcionamiento del semáforo

En general, para usar un semáforo, el subproceso que quiere acceder al recurso compartido intenta adquirir un permiso.

  • Si el conteo del semáforo es mayor que cero, entonces el subproceso adquiere un permiso, lo que hace que el conteo del semáforo disminuya.
  • De lo contrario, el hilo se bloqueará hasta que se pueda adquirir un permiso.
  • Cuando el subproceso ya no necesita acceso al recurso compartido, libera el permiso, lo que hace que se incremente el recuento del semáforo.
  • Si hay otro subproceso esperando un permiso, ese subproceso adquirirá un permiso en ese momento.

Java proporciona la clase Semaphore en el paquete java.util.concurrent que implementa este mecanismo, por lo que no tiene que implementar sus propios semáforos.

Diagrama de flujo :
d

Constructores en la clase Semaphore: Hay dos constructores en la clase Semaphore.

Semaphore(int num)
Semaphore(int num, boolean how)

Aquí, num especifica el recuento inicial de permisos. Por lo tanto, especifica la cantidad de subprocesos que pueden acceder a un recurso compartido en cualquier momento. Si es uno, solo un subproceso puede acceder al recurso en un momento dado. De forma predeterminada, a todos los subprocesos en espera se les otorga un permiso en un orden indefinido. Al establecer cómo en verdadero, puede asegurarse de que los subprocesos en espera reciban un permiso en el orden en que solicitaron acceso.

Uso de semáforos como bloqueos ( prevención de condiciones de carrera )

Podemos usar un semáforo para bloquear el acceso a un recurso, cada subproceso que quiera usar ese recurso primero debe llamar a adquirir() antes de acceder al recurso para adquirir el bloqueo. Cuando el subproceso termina con el recurso, debe llamar a release( ) para liberar el bloqueo. Aquí hay un ejemplo que demuestra esto:

// java program to demonstrate 
// use of semaphores Locks
import java.util.concurrent.*;
  
//A shared resource/class.
class Shared 
{
    static int count = 0;
}
  
class MyThread extends Thread
{
    Semaphore sem;
    String threadName;
    public MyThread(Semaphore sem, String threadName) 
    {
        super(threadName);
        this.sem = sem;
        this.threadName = threadName;
    }
  
    @Override
    public void run() {
          
        // run by thread A
        if(this.getName().equals("A"))
        {
            System.out.println("Starting " + threadName);
            try 
            {
                // First, get a permit.
                System.out.println(threadName + " is waiting for a permit.");
              
                // acquiring the lock
                sem.acquire();
              
                System.out.println(threadName + " gets a permit.");
          
                // Now, accessing the shared resource.
                // other waiting threads will wait, until this 
                // thread release the lock
                for(int i=0; i < 5; i++)
                {
                    Shared.count++;
                    System.out.println(threadName + ": " + Shared.count);
          
                    // Now, allowing a context switch -- if possible.
                    // for thread B to execute
                    Thread.sleep(10);
                }
            } catch (InterruptedException exc) {
                    System.out.println(exc);
                }
          
                // Release the permit.
                System.out.println(threadName + " releases the permit.");
                sem.release();
        }
          
        // run by thread B
        else
        {
            System.out.println("Starting " + threadName);
            try 
            {
                // First, get a permit.
                System.out.println(threadName + " is waiting for a permit.");
              
                // acquiring the lock
                sem.acquire();
              
                System.out.println(threadName + " gets a permit.");
          
                // Now, accessing the shared resource.
                // other waiting threads will wait, until this 
                // thread release the lock
                for(int i=0; i < 5; i++)
                {
                    Shared.count--;
                    System.out.println(threadName + ": " + Shared.count);
          
                    // Now, allowing a context switch -- if possible.
                    // for thread A to execute
                    Thread.sleep(10);
                }
            } catch (InterruptedException exc) {
                    System.out.println(exc);
                }
                // Release the permit.
                System.out.println(threadName + " releases the permit.");
                sem.release();
        }
    }
}
  
// Driver class
public class SemaphoreDemo 
{
    public static void main(String args[]) throws InterruptedException 
    {
        // creating a Semaphore object
        // with number of permits 1
        Semaphore sem = new Semaphore(1);
          
        // creating two threads with name A and B
        // Note that thread A will increment the count
        // and thread B will decrement the count
        MyThread mt1 = new MyThread(sem, "A");
        MyThread mt2 = new MyThread(sem, "B");
          
        // stating threads A and B
        mt1.start();
        mt2.start();
          
        // waiting for threads A and B 
        mt1.join();
        mt2.join();
          
        // count will always remain 0 after
        // both threads will complete their execution
        System.out.println("count: " + Shared.count);
    }
}

Producción:

Starting A
Starting B
A is waiting for a permit.
B is waiting for a permit.
A gets a permit.
A: 1
A: 2
A: 3
A: 4
A: 5
A releases the permit.
B gets a permit.
B: 4
B: 3
B: 2
B: 1
B: 0
B releases the permit.
count: 0

Nota: La salida puede ser diferente en diferentes ejecuciones del programa anterior, pero el valor final de la variable de conteo siempre será 0.

Explicación del programa anterior:

  • El programa usa un semáforo para controlar el acceso a la variable de conteo , que es una variable estática dentro de la clase Shared. Shared.count se incrementa cinco veces por el subproceso A y se reduce cinco veces por el subproceso B. Para evitar que estos dos subprocesos accedan a Shared.count al mismo tiempo, el acceso se permite solo después de obtener un permiso del semáforo de control. Una vez que se completa el acceso, se libera el permiso. De esta manera, solo un subproceso a la vez accederá a Shared.count, como muestra el resultado.
  • Observe la llamada a sleep() dentro del método run() dentro de la clase MyThread. Se utiliza para “probar” que los accesos a Shared.count están sincronizados por el semáforo. En run(), la llamada a sleep() hace que el subproceso que invoca haga una pausa entre cada acceso a Shared.count. Esto normalmente habilitaría la ejecución del segundo subproceso. Sin embargo, debido al semáforo, el segundo subproceso debe esperar hasta que el primero haya liberado el permiso, lo que ocurre solo después de que se completan todos los accesos del primer subproceso. Por lo tanto, Shared.count primero se incrementa cinco veces por el subproceso A y luego se reduce cinco veces por el subproceso B. Los incrementos y decrementos no se entremezclan en el código ensamblador.
  • Sin el uso del semáforo, los accesos a Shared.count por parte de ambos subprocesos habrían ocurrido simultáneamente, y los incrementos y decrementos estarían entremezclados. Para confirmar esto, intente comentar las llamadas para adquirir() y liberar() . Cuando ejecute el programa, verá que el acceso a Shared.count ya no está sincronizado, por lo que no siempre obtendrá el valor de conteo 0.

Siguiente: Clase Java.util.concurrent.Semaphore en Java

Este artículo es una contribución de Gaurav Miglani . Si le gusta GeeksforGeeks y le gustaría contribuir, también puede escribir un artículo usando contribuya.geeksforgeeks.org o envíe su artículo por correo a contribuya@geeksforgeeks.org. Vea su artículo que aparece en la página principal de GeeksforGeeks y ayude a otros Geeks.

Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *