理论上可行,实际上不一定

128 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第14天,点击查看活动详情

前言

在预先初始化中,单例类的实例是在类加载时创建的,这是创建单例类的最简单方法,但它有一个缺点,即即使客户端应用程序可能不会使用它,也会创建实例。

如果您的单例类没有使用大量资源,则可以使用这种方法。但是在大多数情况下,Singleton 类是为文件系统、数据库连接等资源创建的。除非客户端调用该getInstance方法,否则我们应该避免实例化。此外,此方法不提供任何异常处理选项。


public class EagerInitializedSingleton {
    
    private static final EagerInitializedSingleton instance = new EagerInitializedSingleton();
    
    //private constructor to avoid client applications to use constructor
    private EagerInitializedSingleton(){}

    public static EagerInitializedSingleton getInstance(){
        return instance;
    }
}



主旨

  • 在单线程环境下工作正常,但是当涉及到多线程系统时,如果多个线程同时处于 if 条件内,则可能会导致问题。它将破坏单例模式,两个线程将获得单例类的不同实例。
  • 但由于与同步方法相关的成本,它降低了性能,尽管我们只需要它用于可能创建单独实例的前几个线程(阅读:Java 同步)。为了每次都避免这种额外的开销,使用了双重检查锁定原则。在这种方法中,同步块在 if 条件中使用,并进行额外检查以确保仅创建单例类的一个实例。
  • 反射可用于破坏上述所有单例实现方法。让我们用一个示例类来看看这个。

您运行上面的测试类时,您会注意到两个实例的 hashCode 不相同,这破坏了单例模式。反射非常强大,并在很多框架中使用,如 Spring 和 Hibernate。

  • 有时在分布式系统中,我们需要在 Singleton 类中实现 Serializable 接口,以便我们可以将其状态存储在文件系统中并在以后的某个时间点检索它。这是一个也实现了 Serializable 接口的小型单例类。