Inicialización perezosa segura para subprocesos en Java

Shubham Vora 12 octubre 2023
  1. Inicialización de objetos en Java
  2. Implementar la inicialización diferida en Java
  3. Use el método sincronizado para la inicialización perezosa segura para subprocesos en Java
  4. Utilice el método de bloqueo de verificación doble para la inicialización perezosa segura para subprocesos en Java
Inicialización perezosa segura para subprocesos en Java

Este artículo discutirá la implementación de la inicialización diferida segura para subprocesos en Java.

Inicialización de objetos en Java

La inicialización diferida es el acto de retrasar la creación de objetos. También puede provocar retrasos en algunas tareas de cálculo o procesos costosos por primera vez.

Hay dos tipos de inicialización de objetos en Java. Estas son la inicialización Eager y la inicialización Lazy.

La inicialización del objeto se produce durante el tiempo de compilación en la inicialización de Eager. En el caso de un proceso largo, esto consume tiempo y memoria.

En la inicialización diferida, la inicialización del objeto ocurre cuando el programa lo necesita. Por lo tanto, ahorra memoria, aumenta la potencia de procesamiento y mejora la eficiencia.

Los subprocesos son componentes menores que pueden ejecutar procesos en paralelo para optimizar el tiempo. Thread Safe es una clase de Java que garantiza la corrección del estado interno de la clase y los valores que devuelven las funciones durante varias llamadas de subprocesos simultáneamente.

Implementar la inicialización diferida en Java

Un método getter verifica si un miembro privado ya tiene algún valor. Si es así, la función lo devuelve; de lo contrario, crea una nueva instancia y la devuelve para la primera ejecución.

Hay dos métodos para realizar la inicialización diferida.

  1. Utilice la palabra clave sincronizar antes del nombre de la función.
  2. Utilice el método de bloqueo de doble verificación o de bloqueo sincronizado.

Escribir código seguro para subprocesos para la inicialización diferida en Java

Una variable puede devolver resultados inesperados cuando varios subprocesos se ejecutan en paralelo. Aquí, necesitamos un código seguro para subprocesos para evitar interbloqueos en Java.

Ejemplo:

Considere un banco donde solo hay un contador de fichas disponible para la distribución de fichas. La tarea aquí es crear una clase Banco solo cuando entra un cliente; el proceso es una inicialización perezosa.

Vamos a crear múltiples subprocesos, digamos t1, t2, y así sucesivamente, para comprender las llamadas de múltiples subprocesos. Cada hilo será un cliente.

Cuando un cliente viene por el token, creamos un objeto Banco y llamamos a operación() con el número del token en el argumento de la función. Ahora, muestre si hay un token disponible y especifique la siguiente acción en el mensaje.

Si el token está disponible, emita el token; de lo contrario, continúe con el próximo cliente.

El programa explica tanto getInstanceSynchronizedWay() como getInstanceSynchronizedBlockWay(). Podemos usar Thread.sleep para garantizar la precisión de salida.

Use el método sincronizado para la inicialización perezosa segura para subprocesos en Java

En este ejemplo, tenemos dos subprocesos de clientes.

La variable _instance comprueba si una instancia es nula. Si la variable es nula, el programa crea una instancia.

La bandera vacía comprueba si el token está disponible o no. Si el token está disponible, el valor del indicador es verdadero.

Después de entregar el token al cliente, el programa establece el valor de la bandera en falso. Por lo tanto, el próximo cliente no obtendrá el token hasta que se complete la operación().

Código de ejemplo:

// Helper class as a Singleton Class
class BankOperation {
  // Private variables
  private static BankOperation _instance;
  private boolean empty = false;
  private String customerName = "default";
  // Displays the instance only during creation
  private BankOperation() {
    System.out.println("Instance Creation Over\n");
  }
  // synchronized method
  public static synchronized BankOperation getInstanceSynchronizedWay() {
    if (_instance == null)
      _instance = new BankOperation();
    return _instance;
  }
  // Check if the token is available
  public boolean isOperationBankEmpty() {
    return empty;
  }
  // When token giving is successful
  public void endOperation() {
    empty = true;
  }
  // Multiple threads access the method below
  public synchronized void operation(String paramCust) {
    // When the token is available. The flag is true.
    if (empty == true) {
      customerName = paramCust;
      // Issue the token to the customer
      System.out.println("Operation - Token is available.\n"
          + "Giving token to - " + customerName);
      empty = false;
    }
    // The token is not available
    else {
      System.out.println("Sorry " + paramCust + ", Counter closed with " + customerName);
    }
  }
}
// Main class
public class Bank {
  // Driver function
  public static void main(String args[]) {
    // synchronized method
    // Create a thread in main()
    Thread t1 = new Thread(new Runnable() {
      // run() for thread 1
      public void run() {
        // Create objects of other classes here

        BankOperation i1 = BankOperation.getInstanceSynchronizedWay();

        System.out.println("Synchronized Method - Instance 1 - " + i1 + "\n");
        // The method with an argument
        i1.endOperation();
        i1.operation("Customer 1");
      }
    });
    // Thread 2
    Thread t2 = new Thread(new Runnable() {
      // run() for thread 2
      public void run() {
        BankOperation i2 = BankOperation.getInstanceSynchronizedWay();
        System.out.println("Synchronized Method - Instance 2 - " + i2 + "\n");
        i2.operation("Customer 2");
      }
    });
    // Starting thread 1
    t1.start();
    // Start thread 2
    t2.start();
  }
}

Producción :

Instance Creation Over
Synchronized Method - Instance 1 - BankOperation@792bbbb1
Synchronized Method - Instance 2 - BankOperation@792bbbb1
Operation - Token is available.
Giving the token to - Customer 1
Sorry Customer 2, Counter closed with Customer 1

En el resultado anterior, los usuarios pueden observar que dos instancias de clientes vienen en paralelo. Entonces, la primera instancia obtiene el token y la segunda instancia recibe un mensaje.

Utilice el método de bloqueo de verificación doble para la inicialización perezosa segura para subprocesos en Java

En este ejemplo, creamos dos subprocesos de clientes. La variable _instanceForDoubleCheckLocking comprueba dos veces si una instancia es nula y el método getInstanceSynchronizedBlockWay() devuelve la nueva instancia.

El valor de la bandera vacío determina la disponibilidad del token. El token está disponible para el cliente si la bandera es verdadera.

La bandera se vuelve falsa después de que el cliente obtiene el token. La ejecución de múltiples subprocesos no puede cambiar ningún valor hasta que el subproceso actual finalice su operación.

Código de ejemplo:

// Helper class
class BankOperation {
  // Private variable declaration
  private static BankOperation _instanceForDoubleCheckLocking;
  private boolean empty = false;
  private String customerName = "default";
  private BankOperation() {
    System.out.println("Instance Creation Over\n");
  }
  // Synchronized Block Method or Double-Checked Locking
  public static BankOperation getInstanceSynchronizedBlockWay() {
    // Check double locking

    if (_instanceForDoubleCheckLocking == null)
      synchronized (BankOperation.class) {
        if (_instanceForDoubleCheckLocking == null)
          _instanceForDoubleCheckLocking = new BankOperation();
      }
    return _instanceForDoubleCheckLocking;
  }
  // The `token` availability check
  public boolean isOperationBankEmpty() {
    return empty;
  }
  // After giving the token set the flag value
  public void endOperation() {
    empty = true;
  }
  // Multiple threads access the method below
  public synchronized void operation(String paramCust) {
    // The flag is true when the `token` is available
    if (empty == true) {
      customerName = paramCust;
      // Give the `token` to the customer
      System.out.println("Operation - Token is available.\n"
          + "Giving token to - " + customerName);
      empty = false;
    }
    // When the `Token` is not available
    else {
      System.out.println("Sorry " + paramCust + ", Counter closed with " + customerName);
    }
  }
}
// Main class
public class Bank {
  // Driver function
  public static void main(String args[]) {
    // Double Checked Locking
    System.out.println("Double Checked locking - Synchronized Block");
    // Thread 3
    Thread t3 = new Thread(new Runnable() {
      // run() for thread 3
      public void run() {
        BankOperation i1 = BankOperation.getInstanceSynchronizedBlockWay();
        System.out.println("Double Checked Locking - Instance 1 - " + i1 + "\n");

        i1.endOperation();
        i1.operation("Customer 1");
      }
    });
    // Thread 4
    Thread t4 = new Thread(new Runnable() {
      // run() for thread 4
      public void run() {
        BankOperation i2 = BankOperation.getInstanceSynchronizedBlockWay();
        System.out.println("Double Checked Locking - Instance 2 - " + i2 + "\n");
        i2.operation("Customer 2");
      }
    });
    t3.start();
    t4.start();
  }
}

Salida 1:

Double Checked locking - Synchronized Block
Instance Creation Over
Double Checked Locking - Instance 1 - BankOperation@1efc89d6
Double Checked Locking - Instance 2 - BankOperation@1efc89d6
Operation - Token is available.
Giving  token to - Customer 1
Sorry Customer 2, Counter closed with Customer 1

Tenga en cuenta que cuando dos instancias de clientes llegan en paralelo, el programa proporciona el token a la primera instancia y notifica al segundo cliente.

Salida 2:

Double Checked locking - Synchronized Block
Instance Creation Over
Double Checked Locking - Instance 2 - BankOperation@282416b6
Double Checked Locking - Instance 1 - BankOperation@282416b6
Sorry Customer 2, the counter closed with the default
Operation - Token is available.
Giving Bank token to - Customer 1

Aquí, el token no está disponible para el cliente 2, y la razón es que falta endOperation() antes de operation(). El cliente 1 obtiene el token porque endOperation() se ejecuta antes que operation().

Este tutorial nos enseñó dos métodos para implementar la inicialización diferida segura para subprocesos en Java. El primer método utiliza la palabra clave synchronized, y la segunda opción es el método de bloque sincronizado.

El método de bloques sincronizados asegura una doble verificación. Los usuarios pueden elegir cualquier forma según el contexto.

Shubham Vora avatar Shubham Vora avatar

Shubham is a software developer interested in learning and writing about various technologies. He loves to help people by sharing vast knowledge about modern technologies via different platforms such as the DelftStack.com website.

LinkedIn GitHub