Programa Java para demostrar la inicialización perezosa segura para subprocesos

Java es un popular lenguaje de programación orientado a objetos utilizado por desarrolladores y codificadores para el desarrollo de sitios web/aplicaciones. La creación de un objeto de clase utilizando una nueva palabra clave se denomina creación de instancias de objetos. Java por defecto permite a los usuarios definir dos formas de creación de instancias de objetos que son Eager y Lazy. El intérprete de Java interpreta el código del programa/script línea por línea mientras se ejecuta, pero el compilador de Java compila el programa completo antes de iniciar la interpretación. En la creación de instancias ansiosa, la creación del objeto ocurre en tiempo de compilación, por lo que no se considera lo suficientemente eficiente cuando tenemos programas grandes o muchas instancias de objetos, ya que es posible que no se usen. Instanciación perezosa significa instanciar un objeto solo cuando es necesario. Esto se considera eficiente ya que ahorra espacio y potencia de procesamiento cuando consideramos programas grandes o muchas instancias de objetos.

Los subprocesos se consideran pequeños componentes individuales de procesamiento que pueden ejecutarse de forma paralela para mejorar/acelerar el tiempo de cálculo. Con las computadoras multinúcleo avanzadas, la creación de subprocesos ha encontrado una amplia gama de aplicaciones en la programación. En el contexto de Java, un código seguro para subprocesos es aquel que no proporciona resultados anómalos independientemente de cuántas veces se ejecute. El código seguro para subprocesos es muy esencial para evitar defectos/fallas de programación como condiciones de carrera, interbloqueos, etc. 

Métodos:

Volviendo a la creación de instancias perezosa, que a menudo se denomina indistintamente como creación de instancias bajo demanda, tiene 2 métodos para implementarse a sí mismo, son los siguientes:

  1. El uso de palabras clave sincronizadas en todo el método/función es bueno cuando el tamaño del método es pequeño y no muchos subprocesos accederán a él pronto, ya que el procesamiento de cómputo es pesado.
  2. La siguiente y más eficiente forma es la forma de bloque sincronizado en la que simplemente envuelve ese código que debe ser seguro para subprocesos y, para garantizar una mayor seguridad, lo envolvemos con bloqueo verificado dos veces. El siguiente enfoque del estudio de caso del hospital ayudará a comprender mejor estos conceptos en aplicaciones prácticas.

Acercarse:

El siguiente es el enfoque seguido en la secuencia de comandos para demostrar la creación de instancias Lazy en un entorno seguro para subprocesos (tanto el método sincronizado como el bloque sincronizado, es decir, se cubre el bloqueo verificado dos veces) con la ayuda del estudio de caso de operación hospitalaria es el siguiente:

  1. Supongamos que hay un hospital que tiene potencialmente 1 quirófano en el que se realizarían las cirugías a los pacientes.
  2. El problema aquí es que nos gustaría crear un objeto de clase Hospital solo cuando sea necesario (cuando haya un paciente cuya operación deba realizarse), de lo contrario, el objeto Hospital no se crearía; esto se conoce como instanciación diferida.
  3. Para demostrar un entorno de subprocesos múltiples, tenemos varios subprocesos (t1, t2, t3 y t4) que se ilustran como pacientes.
  4. Entonces, digamos que si un paciente cuya operación debe realizarse, hacemos el objeto Hospital y llamamos al método operation() que toma el nombre del paciente como argumento.
  5. Luego, si el quirófano está vacío o no, se mostrará el mensaje apropiado para prepararse para la operación o cuya cirugía se está realizando actualmente, respectivamente.
  6. Para hacer las cosas más realistas y explicar el concepto de bloqueo de doble verificación, estamos demostrando las dos formas de instanciaciones perezosas, que son métodos sincronizados (getInstanceSynchronizedWay()) que, como se explicó anteriormente, es un poco menos eficiente/más costoso que la forma de bloque sincronizado que es getInstanceSynchronizedBlockWay()
  7. Además , hay algunos Thread.sleep()comentar dentro del código con el fin de demostrar la seguridad de subprocesos/ejecución en serie de subprocesos y el seguimiento de la salida es una forma controlada que se explicaría con más detalle en la sección de explicación.

Implementación:

El siguiente es el escenario en el que hay un hospital con un solo quirófano. Entonces, si el quirófano está vacío, lleve al paciente a la operación; de lo contrario, muestre que la operación lamentable se lleva a cabo para otro paciente. Hay varios subprocesos creados en este script con la ilustración de cada subproceso como paciente.

Ejemplo:

Java

// Java Program to Demonstration of Lazy Instantiation in
// Thread-Safe Environment Using synchronized method and
// Double-Checked Locking
 
// Class 1
// Helper class acting as Singleton Class
class HospitalOperation {
 
    // Private class variables
    private static HospitalOperation _instance;
    private static HospitalOperation
        _instanceForDoubleCheckLocking;
    private boolean empty = false;
    private String patientName = "default";
 
    // Method 1
    // Displays Instance created only when new Instance is
    // Created
    private HospitalOperation()
    {
        System.out.println("Instance Created");
    }
 
    // Method 2
    // Synchronized method() Approach
    public static synchronized HospitalOperation
    getInstanceSynchronizedWay()
    {
 
        if (_instance == null)
            _instance = new HospitalOperation();
 
        return _instance;
    }
 
    // Method 3
    // Double Checked Locking- Synchronized Block
    public static HospitalOperation
    getInstanceSynchronizedBlockWay()
    {
 
        // Checking for double locking
        if (_instanceForDoubleCheckLocking == null)
            synchronized (HospitalOperation.class)
            {
                if (_instanceForDoubleCheckLocking == null)
                    _instanceForDoubleCheckLocking
                        = new HospitalOperation();
            }
 
        return _instanceForDoubleCheckLocking;
    }
 
    // Method 4
    // Checks if operation theatre is empty or not
    public boolean isOperationTheatreEmpty()
    {
        return empty;
    }
 
    // Method 5
    // Called when Operation is finished
    public void endOperation() { empty = true; }
 
    // Method 6
    // Accessed by more than one threads
    public synchronized void operation(String aName)
    {
 
        // When flag variables changes from false to true
        if (empty == true) {
            patientName = aName;
 
            // Get the patient ready as operation can be
            // performed
            System.out.println("Operation can be done "
                               + "get ready patient "
                               + patientName);
            empty = false;
        }
 
        // Operation can not be performed
        else {
            // Print and display
            System.out.println(
                "Sorry " + aName
                + " Operation Theatre is busy with Surgery of "
                + patientName);
        }
    }
}
 
// Class 2
// Main class
public class Hospital {
 
    // Main driver method
    public static void main(String args[])
    {
 
        // Synchronized method
 
        // Now creating a thread in main() method
        Thread t1 = new Thread(new Runnable() {
            // run() method for this thread
            public void run()
            {
 
                // Creating object of above class in
                // this class main() method
                HospitalOperation i1
                    = HospitalOperation
                          .getInstanceSynchronizedWay();
 
                // Print statement only
                System.out.println(
                    "The instance 1 in Synchronized Method is "
                    + i1);
 
                // Calling the method
                // passing custom argument as input
                i1.endOperation();
                i1.operation("123");
            }
        });
 
        // Thread 2
        // Again creating another thread
        Thread t2 = new Thread(new Runnable() {
            // run() method for this thread
            public void run()
            {
 
                HospitalOperation i2
                    = HospitalOperation
                          .getInstanceSynchronizedWay();
 
                System.out.println(
                    "The instance 2 in Synchronized Method is "
                    + i2);
                i2.operation("789");
            }
        });
 
        // We delay thread also to ensure that
        // sequence of output is correct
 
        // Starting the first thread
        // using start() method for threads
        t1.start();
 
        // try {
        //     Thread.sleep(1000);
        // }
        // catch (InterruptedException e)
        //     {}
 
        //  Similarly, starting the second thread
        t2.start();
 
        // Double Checked Locking
 
        // Print statement only
        System.out.println(
            "Double Checked locking - Synchronized Block only");
 
        // Thread 3
        // Again creating a thread using runnable interface
        Thread t3 = new Thread(new Runnable() {
            // run() method for this thread
            public void run()
            {
 
                HospitalOperation i1
                    = HospitalOperation
                          .getInstanceSynchronizedBlockWay();
 
                System.out.println(
                    "The instance 1 in Double Checked Locking way is "
                    + i1);
 
                i1.endOperation();
                i1.operation("ABC");
            }
        });
 
        // Thread 4
        // LAstly creating another thread
        Thread t4 = new Thread(new Runnable() {
            // run() method for this thread
            public void run()
            {
                HospitalOperation i2
                    = HospitalOperation
                          .getInstanceSynchronizedBlockWay();
 
                System.out.println(
                    "The instance 2 in Double Checked Locking way is "
                    + i2);
 
                i2.operation("XYZ");
            }
        });
        // We delay thread also to ensure that
        // sequence of output is correct
        // try {
        //     Thread.sleep(1000);
        // }
        // catch (InterruptedException e)
        //     {}
        t3.start();
        // try {
        //     Thread.sleep(1000);
        // }
        // catch (InterruptedException e)
        //     {}
        t4.start();
    }
}

 
Explicación: La siguiente es la explicación del estudio de caso anterior Hospital con un quirófano,

  1. Inicialmente, se crean 2 subprocesos, a saber, t1 y t2 (ilustrados como pacientes ) en el método main() (dentro de la clase Hospital ) y luego esos subprocesos se ejecutan simultáneamente con el programa.
  2. Luego, estos subprocesos llaman al método sincronizado ( getInstanceSynchronizedBlockWay() ) para crear/usar la clase Lazy Instanciation of HospitalOperation para realizar una operación en pacientes llamados 123 y 789 respectivamente.
  3. Inicialmente digamos que la operación/cirugía del paciente predeterminado se estaba realizando en el quirófano. Por lo tanto, hasta que no se llame al método endOperation() antes del método operation() , estará ocupado y mostrará el mensaje de disculpa de acuerdo con el paciente llamado 789 (subproceso t2 ), pero para el nombre del paciente 123 (subproceso t1 ), ya que el método endOperation() es llamado antes de la llamada al  método de operación() , 123 sería aceptado para la cirugía en la operación que muestra el mensaje apropiado tan pronto como en las capturas de pantalla de salida.
  4. Exactamente el mismo proceso que el anterior se repite para los pacientes ABC y XYZ (subprocesos t1 y t2 ) para demostrar el concepto de bloqueo de verificación doble que es una forma de bloque sincronizado de realizar la instanciación diferida del objeto.
  5. Una cosa a tener en cuenta es que la identificación del objeto de la clase HospitalOperation se imprime para t1 y t2 , que es la misma que están demostrando el enfoque del método sincronizado y la identificación del objeto es la misma para t3 y t4 y también están demostrando el método de bloque sincronizado que deja en claro que la clase HospitalOperation actúa como una clase Singleton para cada estrategia de tipos de instanciación perezosos.
  6. Además, cuando los métodos Thread.sleep() no están comentados del código, la salida se produce casi como si los subprocesos se procesaran en serie y no en paralelo, lo que garantiza la misma lógica comercial (comparando las figuras 1 y 2 de las capturas de pantalla de salida de muestra) se encuentra en la salida que demuestra que el script es seguro para subprocesos.

Producción:
 

Figura 1: Entorno de subprocesos múltiples sin Thread.sleep() — Demostración segura para subprocesos

Figura 2: Entorno de subprocesos múltiples con Thread.sleep(): para una mejor legibilidad de salida: demostración de subprocesos seguros

Explicación de salida:

Por lo tanto, el artículo anterior explica la creación de instancias perezosas (se cubren tanto el bloque sincronizado como el bloqueo de doble verificación y los enfoques de métodos sincronizados) de las clases Java (clases Singleton) con la ayuda del ejemplo de estudio de caso Hospital-Operation.

Publicación traducida automáticamente

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