P392 单例模式饿汉式和懒汉式

单例模式

何为单例模式?

在Java中,单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。这个模式在多个场景中非常有用,尤其是当你需要确保某个类只有一个实例,或者需要频繁实例化而又消耗大量资源的对象时。

单例模式的实现通常包含以下几个要点:

  1. 私有构造函数:类的构造函数被声明为私有,防止外部通过new关键字创建实例。
  2. 私有静态实例:在类内部定义一个私有的静态实例变量。
  3. 公共静态方法:提供一个公共的静态方法来获取单例实例。如果实例不存在,则创建它;否则返回已存在的实例。

下面是一个简单的单例模式的实现示例:

public class Singleton {
// 私有静态实例
private static Singleton instance;
// 私有构造函数
private Singleton() {}
// 公共静态方法,获取单例实例
public static Singleton getInstance() {
if (instance == null) {
// 线程安全的延迟初始化
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
// 其他方法...
}

注意:上面的实现是线程安全的,但它在每次调用getInstance()方法时都会进行同步,这可能会降低性能。如果确定你的代码在单线程环境中运行,或者不需要考虑线程安全问题,可以去掉内部的synchronized块。但在多线程环境中,你需要确保线程安全。

其他实现方式

  • 饿汉式:在类加载时就初始化实例,这是线程安全的,但可能会浪费一些内存(如果实例从未被使用)。
  • 双重检查锁定(Double-Check Locking):上面的示例就是使用了双重检查锁定,它结合了饿汉式和懒汉式的优点,既实现了延迟加载,又保证了线程安全。
  • 静态内部类:利用Java的类加载机制实现延迟加载和线程安全。
  • 枚举:Java 5引入的枚举也可以实现单例模式,这是推荐的方式之一,因为它提供了线程安全性和反序列化时的单例保证。

无论选择哪种实现方式,都要确保你的单例模式是线程安全的,并且符合你的应用需求。

饿汉式

在Java中,饿汉式(Eager Initialization)是单例模式的一种实现方式。这种方式在类加载的时候就完成了实例的初始化,因此也被称为立即初始化。由于JVM在加载类时会保证类的初始化操作是线程安全的(即类的初始化只会被执行一次),所以饿汉式实现天然就是线程安全的,无需额外的同步措施。

饿汉式实现单例模式的代码通常如下:

public class Singleton {
// 在类加载时就初始化了实例
private static final Singleton instance = new Singleton();
// 私有构造函数,防止外部通过new创建实例
private Singleton() {}
// 提供全局的访问点来获取该实例
public static Singleton getInstance() {
return instance;
}
// 其他方法...
}

这种方式的优点:

  1. 线程安全:由于JVM类加载机制的保证,实例的初始化只会在类加载时发生一次,所以不存在线程安全问题。
  2. 简单高效:代码实现简单,获取实例的操作也非常高效,没有额外的同步开销。

这种方式的缺点:

  1. 可能会浪费资源:如果单例对象占用的资源较多,并且长时间不被使用,那么这部分资源可能会被浪费。
  2. 可能存在初始化顺序问题:在复杂的系统中,如果Singleton类依赖于其他类,并且这些类还没有被加载和初始化,那么可能会出现初始化顺序的问题。

尽管如此,饿汉式实现单例模式仍然是很多情况下的首选,因为它简单、高效且线程安全。在大多数场景中,资源浪费的问题可以通过合理的资源管理和垃圾回收机制来解决,而初始化顺序问题也可以通过合理的类设计和依赖注入来避免。

懒汉式

Java中的懒汉式(Lazy Initialization)单例模式实现了一种延迟加载的策略,即只有在第一次需要单例实例时才会进行实例化。这种方式在资源消耗较大或者实例化过程较复杂的场景下特别有用,因为它可以避免不必要的资源浪费。

然而,懒汉式实现的一个主要挑战是线程安全性。在多线程环境下,如果没有适当的同步措施,可能会导致多个线程同时创建单例实例,从而违反单例模式的初衷。

下面是一个线程安全的懒汉式单例模式的示例代码:

public class Singleton {
// 使用volatile关键字保证instance在多线程环境下的可见性
private volatile static Singleton instance;
// 私有构造函数,防止外部通过new创建实例
private Singleton() {}
// 提供全局的访问点来获取该实例
public static Singleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (Singleton.class) { // 同步块,确保线程安全
if (instance == null) { // 第二次检查
instance = new Singleton(); // 如果instance为null,则创建实例
}
}
}
return instance;
}
// 其他方法...
}

在这个示例中,我们使用了双重检查锁定(Double-Check Locking)来确保线程安全。第一次检查instance是否为null是为了避免不必要的同步开销,因为当instance已经被创建后,后续的请求都不需要进入同步块。如果instancenull,则进入同步块进行第二次检查,这是为了防止多个线程同时进入同步块并创建多个实例。

另外,我们使用了volatile关键字来修饰instance变量,这是为了确保instance在多线程环境下的可见性。因为volatile会禁止指令重排序,保证在instance = new Singleton();这句代码中,Singleton对象的构造和instance引用的赋值这两个操作是原子性的,不会被其他线程重排序导致的问题所影响。

这种懒汉式单例模式实现方式既保证了线程安全,又避免了不必要的同步开销,是一种比较常用的单例模式实现方式。

  • 15
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值