📜  演示延迟初始化线程安全的Java程序

📅  最后修改于: 2022-05-13 01:55:04.036000             🧑  作者: Mango

演示延迟初始化线程安全的Java程序

Java是一种流行的面向对象的编程语言,被开发人员和编码人员用于网站/应用程序开发。使用类对象的创建 新关键字称为对象实例化。 Java默认允许用户定义两种对象实例化方式,即EagerLazy 。 Java解释器在执行时逐行解释程序/脚本代码,但Java编译器在开始解释之前编译完整的程序。在急切实例化中,对象创建发生在编译时,因此当我们有大型程序或许多对象实例化时,它被认为不够高效,因为它们可能不会被使用。延迟实例化意味着仅在需要时实例化对象。这被认为是有效的,因为当我们考虑大型程序或许多对象实例时,它可以节省空间和处理能力。

线程被视为处理的小型独立组件,可以以并行方式执行以增强/缩短计算时间。随着先进的多核计算机,线程在编程中得到了广泛的应用。在Java的上下文中,线程安全代码是一种无论执行多少次都不会提供异常输出的代码。线程安全代码对于避免诸如竞争条件、死锁等编程缺陷/缺陷非常重要。

方法:

回到惰性实例化,它通常可互换地称为按需实例化,有两种实现自身的方法,如下所示:

  1. 当方法大小很小并且由于计算处理繁重而没有多少线程会立即访问它时,在整个方法/函数上使用同步关键字是很好的。
  2. 下一个更有效的方法是同步块方式,您只需将需要线程安全的代码包装起来,并确保我们使用双重检查锁定来确保更可靠。以下医院案例研究的方法将有助于在实际应用中更好地理解这些概念

方法:



以下是脚本中在线程安全环境(同步方法和同步块,即双重检查锁定)中演示Lazy实例化的方法,并借助hospital-Operation案例研究如下:

  1. 假设有一家医院可能有 1 个手术室,其中将对患者进行手术。
  2. 这里的问题是我们只想在需要时(当有需要执行手术的病人时)创建一个Hospital类对象,否则Hospital 对象不会被创建——这被称为惰性实例化
  3. 为了演示多线程环境,我们将各种线程(t1、t2、t3 和 t4)作为患者进行说明。
  4. 因此,假设需要执行手术的患者,我们创建 Hospital 对象,然后调用operation() 方法,该方法将患者姓名作为参数。
  5. 然后,如果手术室是空的或不是空的,则将分别显示关于准备好手术或当前正在执行谁的手术的适当消息。
  6. 为了使事情更现实并解释概念 双重检查锁定 我们正在演示延迟实例化的两种方法,它们是同步方法(getInstanceSynchronizedWay()) ,如上所述,它比同步块方法getInstanceSynchronizedBlockWay()效率低一些/成本更高
  7. 此外还有一些Thread.sleep() 为了演示线程的线程安全/串行执行和跟踪输出而在代码内部进行注释是一种受控方式,将在解释部分进一步解释。

执行:

下面是一个医院只有一个手术室的场景。因此,如果手术室是空的,则让患者进行手术,否则会显示为另一名患者进行抱歉的手术。此脚本中创建了多个线程,每个线程都作为患者进行了说明。

例子:

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()
            {
 
                // Creaating 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 runnabke ineterface
        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 anotherr thread
        Thread t4 = new Thread(new Runnable() {
            // run() method for thi 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();
    }
}


说明:以下为以上案例医院一手术室的说明,

  1. 最初,2个线程即t1t2(患者图示)在main()方法被创建(医院类中),然后将这些线程与程序同时运行。
  2. 然后这些线程调用同步方法 ( getInstanceSynchronizedBlockWay() ) 方式来创建/使用HospitalOperation类的延迟实例化,分别对名为123789 的患者执行操作。
  3. 最初说默认患者的手术/手术是在手术室进行的。因此,直到endOperation()方法在operation()方法之前没有被调用之前,它会很忙并相应地向名为789的患者(线程t2 )显示抱歉消息,但对于患者姓名123 (线程t1 ),因为endOperation()方法是在operation()方法调用之前调用, 123将被接受为手术中的手术,尽快在输出屏幕截图中显示适当的消息。
  4. 对患者ABCXYZ (线程t1t2 )重复与上述完全相同的过程,以演示双重检查锁定的概念,这是执行对象延迟实例化的同步块方式。
  5. 需要注意的一件事是为t1t2打印了HospitalOperation类的对象 id,这与它们演示的同步方法方法相同,并且对象 id 与t3t4相同,并且它们正在演示同步块方法明确说明HospitalOperation类对于惰性实例化类型的每个策略都充当单例类。
  6. 此外,当Thread.sleep()方法从代码中取消注释时,输出几乎就像线程正在串行处理而不是以并行方式处理,这确保了相同的业务逻辑(比较示例输出屏幕截图的图 12 )在输出中找到,表明脚本是线程安全的。

输出:

图 1:没有 Thread.sleep() 的多线程环境——线程安全演示

图 2:使用 Thread.sleep() 的多线程环境——为了更好的输出可读性——线程安全演示

输出说明:

因此,上面的文章在 Hospital-Operation 案例研究示例的帮助下解释了Java类(Singleton 类)的延迟实例化(包括同步块以及双重检查锁定和同步方法方法)。