📜  演示惰性初始化非线程安全的Java程序

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

演示惰性初始化非线程安全的Java程序

近年来,面向对象编程已经形成了网站和应用程序(软件)开发的支柱/基础。 Java和Python是流行的面向对象编程语言,其中前者由 Oracle 提供和维护,而后者是开源的。 Java以及强大且易于设置的框架(如 spring 框架)使软件开发的集成和配置部分变得非常容易和优化。 Java面向对象的四个核心支柱之一被称为封装,这意味着将通常可互换地称为函数的实例变量和方法绑定到一个类中。使用这些实例变量和方法使用 new 关键字创建类对象的过程在Java称为实例化。我们有两种在Java中进行对象实例化的方法,即 Eager 和 Lazy,两者在现实世界中都有其实际应用。

延时实例也被称为按需实例化,其中的类的对象被实例化仅在需要时(即动力学)。当我们谈论大型软件程序时,这有助于我们节省计算处理能力和内存。线程就像一个程序的一个小的简单计算函数/进程,可以以并行方式同时运行以提高处理速度。随着一天天的发展,我们正朝着具有高 GPU/时钟速度的多核强大处理系统发展,多线程已经发现了广泛的编程应用。线程安全环境/代码是这样一种代码,即使运行不止一次(多次),也会产生所需的业务逻辑输出,并且不会显示异常行为。本文试图帮助我们理解惰性实例化在非线程安全环境中的实际世界中是如何工作的,以及如果我们没有线程安全代码会出现什么问题。

方法:

随着餐厅表预订一个简单的个案研究的帮助下,我们会试着去了解延时实例是如何工作的,并会发生什么非线程-安全的代码在多线程环境。以下是我们案例研究餐厅餐桌预订的方法,

  1. 程序中有一个Restaurant类,其中包含 main() 方法,并充当可以在线预订/预订餐桌的餐厅。
  2. 表 1显示为Singleton班级,是这家餐厅的在线预订表之一。
  3. 程序中创建了各种线程,这些线程被说明为试图通过 Table1 类的延迟实例化来预订/预订 table1 的客户。
  4. 有一个简单的检查,看看 table1 是否已经为另一个客户保留/预订,如果是,则显示当前客户请求的抱歉消息以及 table1 已经为其预订,否则 tabke1 是为当前客户名称预订的.
  5. 然后我们有能力同时运行两个线程,我们演示了异常行为是如何引起的,因为代码不是线程安全的,说明如果两个客户试图同时预订/预订 table1,那么他们都可能会得到一个预订成功消息,但不幸的是,table1 将仅为其中 1 人保留/预订。
  6. 最后,我们引入Thread.sleep()来延迟启动第三个线程(客户)谁得到表的正确消息已经被保留/预订,这表明 Lazy Instantiation 工作得很好,但代码是非线程的- 在多线程环境中安全。

实施:餐厅餐桌预订



下面的 Restaurant-Table 预订案例研究将帮助我们理解 Lazy-Instantiation 非线程安全。在这个案例研究中,我们有一个餐厅,有一张名为 Table1 的桌子,可用于在线预订/预订。制作了各种线程,将其描绘为试图预订或预订餐桌的客户1。在实际世界中,如果 table1 未预订,则应为第一个客户请求预订并应相应显示。如果它已经被预订,它应该显示抱歉它已经为其他客户预订(指定其名称)。我们将看到如果两个线程(客户)同时尝试同时预订桌子,则会导致错误。

例子

Java
// Java Program to Demonstrate the Lazy initialization
// non-thread-safe
 
// Importing input output classes
import java.io.*;
 
// Class 1
// Helper class behaving as a Singleton Class
class Table1 {
 
    // Lazy Instantiation also referred as On-demand
    // Instantiation
 
    // Private static member variables
    private static Table1 table1;
    private static String customerNameBooked;
 
    // Constructor of this class which is private
    // To display customer name whom table1 is booked for
    private Table1(String customerName)
    {
 
        // Print and display the customer name
        System.out.println("Table1 is now Booked for "
                           + customerName);
 
        // Booking under the same person
        customerNameBooked = customerName;
    }
 
    // Non thread-safe block of code to
    // demonstrate thread safe with updation in its methods
 
    // Method 1
    // To get the status of table
    public static Table1
    getTable1Instance(String customerName)
    {
 
        // If table is nor book/reserve
        if (table1 == null) {
 
            // book under the derired customer name
            table1 = new Table1(customerName);
        }
 
        // If table is already booked
        else
 
            // Calling th method
            tableBooked(customerName);
 
        return table1;
    }
 
    // Method 2 (auxiliary)
    // To display whom table is booked for
    private static void tableBooked(String customerName)
    {
 
        // Print the custom name and
        // name of customer under which table i booked
        System.out.println(
            "Sorry " + customerName
            + " Table 1 is already Booked for "
            + customerNameBooked);
    }
}
 
// Class 2
// Main class
public class Restaurant {
 
    // Main driver method
    public static void main(String args[])
    {
 
        // Now we will be creating various threads as
        // customer who wish to book Table1 in Restaurant
 
        // Creating first customer(Thread-0)
        // using Runnable interface
        Thread t1 = new Thread(new Runnable() {
            // run() method for the thread
            public void run()
            {
 
                // Getting the table status
                Table1 customer1
                    = Table1.getTable1Instance("ABC");
            }
        });
 
        // Similarly repeating same for other customers
 
        // Again creating second customer(Thread-1)
        // using Runnable interface
        Thread t2 = new Thread(new Runnable() {
            // run() method for this thread
            public void run()
            {
 
                Table1 customer2
                    = Table1.getTable1Instance("XYZ");
            }
        });
 
        // Creating third customer(Thread-2)
        // using Runnable interface
        Thread t3 = new Thread(new Runnable() {
            // run() method for this thread
            public void run()
            {
 
                Table1 customer3
                    = Table1.getTable1Instance("PQR");
            }
        });
 
        // Now starting the threads
        // using start() method
        t1.start();
        t2.start();
 
        // Try block to check for exceptions if any
        try {
 
            // Intentionally having a Thread.sleep(1000) to
            // demonstrate not Thread-safe environment
            Thread.sleep(1000);
        }
 
        // Catch block to handle the exceptions
        catch (InterruptedException e) {
        }
 
        // Now starting the last thread via same means
        t3.start();
    }
}



输出:

图 1:非线程安全环境中的延迟实例化演示。

输出说明:上面的脚本代码执行显示出来,我们将收集所有方面来了解它是如何非线程安全的。

  • 最初,我们有 3 个线程,分别是t1、t2 t3分别显示为名称分别为ABC、XYZPQR 的客户,他们试图在Restaurant类中预订/预订Table1
  • t1t2在线程中同时启动,以演示多线程环境。两者都调用方法getTable1Instance()来预订表,这里Table1类的行为类似于Java的单例类,其实例只能创建一次,说明 table1 一次只能被 1 个客户占用。
  • 但是由于代码是非线程安全的,因此我们为两个客户(即ABCXYZ )预订了表,因为我们可以看到 table1 的实例被创建了两次,尽管它被标记为具有单例惰性实例化属性班级。
  • 然后我们通过使用Thread.sleep()在开始执行线程t3 (客户名称为PQR )时引入延迟,因此我们现在可以看到根据业务逻辑,显示给PQR的消息是正确的预订它应该只向当前客户显示抱歉的消息,并显示为哪个客户预订了桌子。这表明延迟实例化在非线程安全代码的线程的串行执行中运行良好。
  • 如果有多个线程以并行方式工作,那么代码会通过为两个试图同时预订的客户预订同一个桌子实例来显示异常行为,但最终只预订了一个名字为 then 的客户显示线程t3 ( PQR ),如下图 1 所示,这是示例输出屏幕截图。

结论:因此在一个帮助下 在非预约延时实例化的概念餐厅表的简单案例研究-线程安全的环境/上面的代码解释。