Java 中的单例类 | 如何创建单例设计模式

x33g5p2x  于2021-10-14 转载在 Java  
字(3.3k)|赞(0)|评价(0)|浏览(274)

Java 中的单例类是什么?

通俗地说,Java 中的 Singleton 类是允许一次通过单个实例访问它的类。这种设计模式用于限制类的不必要实例化,并确保每个 JVM 实例在任何时间点都只存在类的一个对象。因此,使用此模式,任何定义为 Singleton 的类都只有一个实例,并具有对其的全局访问点。与普通类不同,单例类不会在应用程序生命周期结束时销毁。

但是为什么我们首先需要一个单例类呢?

好吧,通过限制类的实例创建,它节省了内存空间,因为现在每次发出新请求时都不会创建对象。相反,将重复使用单个对象。这就是 Java 中的单例模式主要用于多线程和数据库应用程序的原因。它主要用于日志记录、缓存、线程池、配置设置等等。

我希望您清楚 Java 中单例类的概念。所以,现在让我们在这篇 Java 中的单例类文章中进一步了解它们是如何创建的。

设计 Java 单例类的方法

为了在 Java 中创建类单例,您需要以下三样东西:

1.类的静态成员
1.私有构造函数
1.静态工厂方法

由于 Java 允许开发人员探索他们的视野,因此您可以通过多种方式设计 Singleton 类。下面我列出了最受欢迎的。

  1. Eager 初始化方法
  2. 延迟初始化方法
    1.线程安全的单例方法
  3. 使用双锁方法的延迟初始化
  4. 懒加载方法
    1.静态块初始化方法

现在让我们一一深入研究这些方法中的每一种。

1。 Eager 初始化方法

这是创建单例类的最简单方法,其中实例在类加载时创建。要使用此方法创建单例类,您需要按照以下步骤操作:

  1. 将构造函数声明为私有。
  2. 下一步是为这个 Singleton 类创建一个私有类成员。
  3. 现在,您需要定义一个工厂方法,该方法将用于返回您的类的对象,我们已将其创建为类成员的实例。
    1.如果你想直接访问这个静态实例,你甚至可以声明一个静态成员public。

现在,让我们看看如何实现这些。

//Eager Initialization
public class EagerSingleton {
    private static final EagerSingleton INSTANCE = new EagerSingleton();
    private EagerSingleton() {}
  public static EagerSingleton getInstance() {
      return INSTANCE;
    }
}

如果您看到代码,您可以观察到每次我们实例化一个对象时,我们都在使用 getInstance() 方法而不是调用类构造函数。但它有其自身的缺点。如果您使用此方法创建类单例,则无论应用程序是否正在使用它,都会创建一个实例。

所以,让我们继续看另一种在 Java 中创建单例类的方法。

2。延迟初始化方法

此方法称为延迟初始化,因为它将类实例的创建推迟到第一次使用。我的意思是,使用这种方法,只有在需要时才会创建对象。它有助于避免不必要地创建类实例。要以这种方式设计单例类,您需要遵循以下列出的步骤:

  1. 首先,将构造函数声明为私有。
  2. 然后你需要为这个类创建一个私有的静态实例,但不需要实例化它。
  3. 最后,创建一个工厂方法,首先检查实例成员是否为空。如果没有,它将为您创建一个单例类的实例并返回它。

下面的代码显示了如何执行此操作。

//Lazy Initialization
public class LazySingleton {
    private static LazySingleton INSTANCE = null;
    private LazySingleton() {}
    public static LazySingleton getInstance() {
        if (INSTANCE == null) {  
          synchronized(LazySingleton.class) {
        	  INSTANCE = new LazySingleton();
          }
        }
        return INSTANCE;
    }
}

3。线程安全单例****方法

但是上述方法在并发场景中会引起一些担忧。由于单例模式主要用于多线程,并且如果多个线程同时进入 if 条件,则可能会引发问题。为了避免这种情况,我们尝试通过使全局访问方法同步来创建线程安全的单例类。这确保在任何时间点只有一个线程正在执行此方法。参考以下代码查看实现:

//Thread Safe Singleton
public class ThreadSafeSingleton {

    private static ThreadSafeSingleton INSTANCE;
    
    private ThreadSafeSingleton(){}
    
    public static synchronized ThreadSafeSingleton getInstance(){
        if(INSTANCE == null){
        	INSTANCE = new ThreadSafeSingleton();
        }
        return INSTANCE;
    }
    
}

但有时这种方法也会变得非常麻烦,因为每次调用该方法时,它都需要等待锁被释放,然后该方法才能使用它。这会导致进程变慢,并导致我们采用下一种方法,即带双锁的延迟初始化。

4。双锁延迟初始化****方法

在这种方法中,我们不同步方法。相反,我们将对象创建代码封装在一个同步块中。可以说,通过预先检查线程锁,它减少了获取锁的次数。这种方法通常会提高应用程序的性能。查看下面的代码,看看它是如何完成的。

//Lazy Initialization with Double Lock
public class LazyDoubleLockSingleton {
    private static LazyDoubleLockSingleton INSTANCE = null;
    private LazyDoubleLockSingleton() {}
    public static LazyDoubleLockSingleton getInstance() {
        if (INSTANCE == null) {
            synchronized (LazyDoubleLockSingleton.class) {
                if (INSTANCE == null) {
                    INSTANCE = new LazyDoubleLockSingleton();
                }
            }
        }
        return INSTANCE;
    }
}

5。延迟加载方法

此方法基于 JSL(Java 语言规范),根据此 JVM 将仅在需要时加载静态数据成员。因此,当您的单例类加载到 JVM 中时,不会创建任何实例。此外,在程序执行期间,全局方法按顺序调用。使用此方法,您不必显式同步静态 getInstance() 以加载和初始化。静态类成员将以适当的顺序调用,全局方法的其余并发调用以相同的顺序返回,而无需执行同步开销。

下面是执行相同操作的代码。

//Lazy Load Method
public class LazyLoadSingleton {
    private LazyLoadSingleton() {}
    private static class SingletonClassHolder {
        static final Var INSTANCE = new LazyLoadSingleton();
    }
    public static LazyLoadSingleton getInstance() {
        return SingletonClassHolder.INSTANCE;
    }
}

6。静态块初始化方法

这种在 Java 中创建单例类的方法类似于 Eager 初始化方法。唯一的区别是此类的实例是在具有异常处理功能的静态块中创建的。

//Static Block Initialization
public class StaticBlockSingleton {

    private static StaticBlockSingleton INSTANCE;
    
    private StaticBlockSingleton(){}
    
    //exception handling within static block
    static{
        try{
        	INSTANCE = new StaticBlockSingleton();
        } catch (Exception e) {
            throw new RuntimeException("Exception occured while creating a Singleton Class");
        }
    }
    
    public static StaticBlockSingleton getInstance(){
        return INSTANCE;
    }
}

相关文章