Uso de Semaphore para proteger más de una copia de un recurso en Java

Un trabajo de semáforo sobre los conceptos de permisos. Un semáforo se inicializa con un cierto número de permisos. El número de permisos es igual al número de recursos compartidos disponibles. Cuando un subproceso quiere acceder a un recurso compartido, adquiere el permiso y cuando termina de acceder al recurso compartido, libera el permiso.

Ilustración:

Ilustración 1: Aplicación de restaurante con 10 mesas.

Hay 20 personas paradas afuera del restaurante, esperando para entrar. En este caso, el número de semáforos es el mismo que el número de recursos (mesas) que es 10. Para que cualquier cliente ingrese al restaurante, debe adquirir un permiso. Después de adquirir el permiso, elige una de las mesas disponibles. Una vez que se completa su pedido, libera el semáforo y lo pone a disposición de otros clientes que esperan en la cola. Así semáforo asegura que a la vez solo 10 clientes puedan entrar al restaurante y hacer pedidos.

Ilustración 2: Personas haciendo cola en un centro de cajeros automáticos. 

Si hay 2 cajeros automáticos, solo 2 personas a la vez pueden acceder al cajero automático y retirar dinero. Cuando un cliente ingresa al centro de cajeros automáticos, adquiere un permiso, si está disponible, y luego verifica cuál de los cajeros automáticos está libre para usar. Una vez que obtiene un cajero automático disponible, lo bloquea y luego ingresa el PIN y retira dinero. Después de retirar, libera la cerradura del cajero automático y también libera el semáforo. La razón por la que tiene que bloquear el cajero automático antes de retirar dinero es si 2 personas después de adquirir el semáforo terminan en el mismo cajero automático. Aquí el cajero automático es un recurso compartido y el número de semáforos es igual al número de cajeros automáticos.

El funcionamiento de un semáforo es el siguiente:

Para nuestra aplicación de restaurante, inicializamos nuestro semáforo con 10 permisos que son iguales al número de mesas disponibles. Si hay 30 clientes en la cola esperando para entrar al restaurante, el Cliente 1 adquiere un permiso, entra al restaurante. Cuando un cliente entra en el restaurante, adquiere un permiso y elige 1 de las 10 mesas disponibles. Supongamos que el cliente #1 elige la mesa #5. El número de permisos disponibles es 9. El cliente #2 adquiere otro permiso ya que todavía hay permisos disponibles y elige la mesa #7. De esta forma, 10 clientes pueden adquirir permisos y elegir las mesas desocupadas . Cuando el cliente nº 11 entra en el restaurante, queda bloqueado hasta que uno de los 10 clientes suelta el semáforo y abandona la mesa.

Ejemplo:

Java

// Java Program to Use a Semaphore to Protect more than One
// Copy of a Resource
 
// Importing input output classes
import java.io.*;
// Importing utility classes
import java.util.Arrays;
import java.util.concurrent.Semaphore;
 
// Class 1
// Helper class 1
class ATMQueue {
 
    // Member variables
    private Semaphore semaphore;
    private boolean[] freeAtms;
 
    // Method 1
    public ATMQueue()
    {
 
        semaphore = new Semaphore(2);
        freeAtms = new boolean[2];
        Arrays.fill(freeAtms, true);
    }
 
    // Method 2
    public void withDrawMoney()
    {
 
        // Try block to check fo exceptions
        try {
 
            // Try to acquire a semaphore.
            // If none are available, thread will block here
            // till a semaphore becomes available
            semaphore.acquire();
 
            // Check for available ATM machine
            int atmMachine = getAvailableATM();
 
            // since atm Machine is available to withdraw
            // money, we acquire a semaphore
            System.out.println(
                Thread.currentThread().getName()
                + ":-Withdrawing money from atm number :-"
                + atmMachine);
 
            System.out.println(
                "---------------------------------");
 
            Thread.sleep((long)(Math.random() * 1000));
 
            System.out.println(
                Thread.currentThread().getName()
                + ":-done withdrawing money");
 
            releaseATM(atmMachine);
 
            System.out.printf(
                "ATM machine :- %s is now available",
                atmMachine);
 
            System.out.println(
                "---------------------------------");
 
            System.out.println(
                "About to release the semaphore");
 
            semaphore.release();
 
            System.out.println("Semaphore released");
        }
 
        // catch block to handle the exceptions
        catch (InterruptedException e) {
 
            // Print the exceptions along with line number
            e.printStackTrace();
        }
    }
 
    // Method 3
    private void releaseATM(int atmNumber)
    {
 
        // We are making specific atm machine free for use
        synchronized (freeAtms)
        {
            freeAtms[atmNumber] = true;
        }
    }
 
    // Method 4
    private int getAvailableATM()
    {
        int freeAtm = -1;
 
        // We are using synchronized to ensure that only 1
        // thread can access and modify the shared boolean
        // array freeAtms
        synchronized (freeAtms)
        {
 
            for (int i = 0; i < freeAtms.length; i++) {
 
                if (freeAtms[i]) {
                    freeAtms[i] = false;
                    freeAtm = i;
 
                    break;
                }
            }
        }
 
        return freeAtm;
    }
}
 
// Class 2
// Helper class 2
class WithdrawMoneyTask implements Runnable {
 
    private ATMQueue atmQueue;
 
    public WithdrawMoneyTask(ATMQueue atmQueue)
    {
        // TODO Auto-generated constructor stub
        this.atmQueue = atmQueue;
    }
 
    @Override public void run()
    {
        System.out.println(
            Thread.currentThread().getName()
            + ": - about to withdraw money after acquiring the permit");
 
        atmQueue.withDrawMoney();
    }
}
 
// Class 3
// Main class
class GFG {
 
    // Main  driver method
    public static void main(String[] args)
    {
 
        // Print statement
        System.out.println("GFG!");
 
        // Creating an object of class 1 in main() method
        ATMQueue atmQueue = new ATMQueue();
 
        // Creating Thread class object
        Thread thread[] = new Thread[10];
 
        for (int i = 0; i < 10; i++) {
            thread[i] = new Thread(
                new WithdrawMoneyTask(atmQueue),
                "Thread " + i);
        }
 
        for (int i = 0; i < 10; i++) {
            thread[i].start();
        }
    }
}

Java

// Java Program to Use a Semaphore to Protect more than One
// Copy of a Resource
 
// Importing input output classes
import java.io.*;
// Importing utility classes
import java.util.Arrays;
import java.util.concurrent.Semaphore;
 
// Class 1
// Helper class 1
class ATMQueue {
 
    // Member variables
    private Semaphore semaphore;
    private boolean[] freeAtms;
 
    // Method 1
    public ATMQueue()
    {
 
        semaphore = new Semaphore(2);
        freeAtms = new boolean[2];
        Arrays.fill(freeAtms, true);
    }
 
    // Method 2
    public void withDrawMoney()
    {
 
        // Try block to check fo exceptions
        try {
 
            // Try to acquire a semaphore.
            // If none are available, thread will block here
            // till a semaphore becomes available
            semaphore.acquire();
 
            // Check for available ATM machine
            int atmMachine = getAvailableATM();
 
            // since atm Machine is available to withdraw
            // money, we acquire a semaphore
            System.out.println(
                Thread.currentThread().getName()
                + ":-Withdrawing money from atm number :-"
                + atmMachine);
 
            System.out.println(
                "---------------------------------");
 
            Thread.sleep((long)(Math.random() * 1000));
 
            System.out.println(
                Thread.currentThread().getName()
                + ":-done withdrawing money");
 
            releaseATM(atmMachine);
 
            System.out.printf(
                "ATM machine :- %s is now available",
                atmMachine);
 
            System.out.println(
                "---------------------------------");
 
            System.out.println(
                "About to release the semaphore");
 
            semaphore.release();
 
            System.out.println("Semaphore released");
        }
 
        // catch block to handle the exceptions
        catch (InterruptedException e) {
 
            // Print the exceptions along with line number
            e.printStackTrace();
        }
    }
 
    // Method 3
    private void releaseATM(int atmNumber)
    {
 
        // We are making specific atm machine free for use
        synchronized (freeAtms)
        {
            freeAtms[atmNumber] = true;
        }
    }
 
    // Method 4
    private int getAvailableATM()
    {
        int freeAtm = -1;
 
        // We are using synchronized to ensure that only 1
        // thread can access and modify the shared boolean
        // array freeAtms
        synchronized (freeAtms)
        {
 
            for (int i = 0; i < freeAtms.length; i++) {
 
                if (freeAtms[i]) {
                    freeAtms[i] = false;
                    freeAtm = i;
 
                    break;
                }
            }
        }
 
        return freeAtm;
    }
}
 
// Class 2
// Helper class 2
class WithdrawMoneyTask implements Runnable {
 
    private ATMQueue atmQueue;
 
    public WithdrawMoneyTask(ATMQueue atmQueue)
    {
        // TODO Auto-generated constructor stub
        this.atmQueue = atmQueue;
    }
 
    @Override public void run()
    {
        System.out.println(
            Thread.currentThread().getName()
            + ": - about to withdraw money after acquiring the permit");
 
        atmQueue.withDrawMoney();
    }
}
 
// Class 3
// Main class
class GFG {
 
    // Main  driver method
    public static void main(String[] args)
    {
 
        // Print statement
        System.out.println("GFG!");
 
        // Creating an object of class 1 in main() method
        ATMQueue atmQueue = new ATMQueue();
 
        // Creating Thread class object
        Thread thread[] = new Thread[10];
 
        for (int i = 0; i < 10; i++) {
            thread[i] = new Thread(
                new WithdrawMoneyTask(atmQueue),
                "Thread " + i);
        }
 
        for (int i = 0; i < 10; i++) {
            thread[i].start();
        }
    }
}
Producción

GFG!
Thread 7: - about to withdraw money after acquiring the permit
Thread 4: - about to withdraw money after acquiring the permit
Thread 9: - about to withdraw money after acquiring the permit
Thread 2: - about to withdraw money after acquiring the permit
Thread 8: - about to withdraw money after acquiring the permit
Thread 0: - about to withdraw money after acquiring the permit
Thread 1: - about to withdraw money after acquiring the permit
Thread 5: - about to withdraw money after acquiring the permit
Thread 3: - about to withdraw money after acquiring the permit
Thread 6: - about to withdraw money after acquiring the permit
Thread 4:-Withdrawing money from atm number :-1
---------------------------------
Thread 7:-Withdrawing money from atm number :-0
---------------------------------
Thread 7:-done withdrawing money
ATM machine :- 0 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 9:-Withdrawing money from atm number :-0
---------------------------------
Thread 4:-done withdrawing money
ATM machine :- 1 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 2:-Withdrawing money from atm number :-1
---------------------------------
Thread 9:-done withdrawing money
ATM machine :- 0 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 8:-Withdrawing money from atm number :-0
---------------------------------
Thread 2:-done withdrawing money
ATM machine :- 1 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 0:-Withdrawing money from atm number :-1
---------------------------------
Thread 0:-done withdrawing money
ATM machine :- 1 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 1:-Withdrawing money from atm number :-1
---------------------------------
Thread 1:-done withdrawing money
ATM machine :- 1 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 5:-Withdrawing money from atm number :-1
---------------------------------
Thread 5:-done withdrawing money
ATM machine :- 1 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 3:-Withdrawing money from atm number :-1
---------------------------------
Thread 3:-done withdrawing money
ATM machine :- 1 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 6:-Withdrawing money from atm number :-1
---------------------------------
Thread 8:-done withdrawing money
ATM machine :- 0 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 6:-done withdrawing money
ATM machine :- 1 is now available---------------------------------
About to release the semaphore
Semaphore released

Además, la salida en tiempo real se agrega debido a la aparición de un semáforo que conduce a la concurrencia en la ejecución de nuestro programa.

 Explicación de salida:

  • En este ejemplo de código, hay 3 clases. La clase WithdrawMoneyTask define el trabajo/tarea a ejecutar. Entonces implementa la interfaz Runnable.
  • Tiene un miembro privado ATMQueue que representa un cajero automático. Lo llamamos método de retiro de dinero().
  • En la clase ATMQueue, tenemos 2 variables miembro. El semáforo de la variable miembro se define en el constructor. Se inicializa a 2 porque solo hay 2 cajeros automáticos. La variable miembro freeAtms es una array booleana que se inicializa en verdadero en el constructor, lo que indica que ambos cajeros automáticos están libres inicialmente.
  • En retirar dinero(), primero adquirimos el semáforo y luego verificamos qué cajero automático está libre. Esto se hace a través de getAvailableATM(). Existe la posibilidad de que 2 hilos adquieran el semáforo e ingresen este bloque de código y modifiquen la variable de recursos compartidos freeAtms. Entonces, el código para verificar si hay cajeros automáticos disponibles y marcarlo como ocupado está en el bloque sincronizado.
  • Una vez que encontramos el cajero automático disponible, retiramos el dinero. Una vez que se retira el dinero, liberamos el cajero automático mediante el método releaseATM(atmNumber). Este código también está sincronizado, para garantizar que solo 1 subproceso ingrese a este bloque y modifique la array booleana de freeAtms. Luego liberamos el semáforo para que otros subprocesos en espera adquieran y retiren dinero según el cajero automático disponible.

Según el resultado, en cualquier momento dado, solo 2 subprocesos pueden adquirir el semáforo y retirar dinero. Si un hilo no puede adquirir un semáforo, se bloquea hasta que uno de los hilos que ha adquirido el semáforo lo libera. Así es como se pueden usar los semáforos para proteger más de una copia de un recurso compartido que en este caso es un cajero automático.

Publicación traducida automáticamente

Artículo escrito por suchitrasrinivas 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 *