设计模式-单例模式

单例模式 (Singleton Pattern)是一种常用的设计模式,确保一个类在整个应用程序中只有一个实例,并且提供一个全局访问点来访问这个实例。它的主要目标是控制实例化,避免创建多个实例,以节省资源并保证全局状态的一致性。

在Java中实现单例模式有几种常见的方式:

1. 懒汉式单例(Lazy Initialization Singleton)

实现方式

懒汉式单例是在需要时才创建实例 (即延迟加载)。这是通过在首次调用 getInstance() 方法时创建对象的。

java 复制代码
public class LazySingleton {
    // 私有静态实例,尚未初始化
    private static LazySingleton instance;

    // 私有构造函数,防止外部实例化
    private LazySingleton() {}

    // 提供获取实例的全局访问点
    public static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();  // 在第一次调用时创建实例
        }
        return instance;
    }
}
优点
  • 实例在需要时才创建,节省资源。
  • 延迟加载,避免程序启动时不必要的实例创建。
缺点
  • 线程不安全 :如果有多个线程同时访问 getInstance(),可能会导致多个实例被创建。需要进行额外的线程同步处理。

2. 线程安全的懒汉式单例

为了解决懒汉式单例的线程安全问题,可以在 getInstance() 方法上加上同步锁。

java 复制代码
public class SynchronizedLazySingleton {
    private static SynchronizedLazySingleton instance;

    private SynchronizedLazySingleton() {}

    // 通过 synchronized 关键字保证线程安全
    public static synchronized SynchronizedLazySingleton getInstance() {
        if (instance == null) {
            instance = new SynchronizedLazySingleton();
        }
        return instance;
    }
}
优点
  • 保证了线程安全,不会出现多个实例。
缺点
  • 每次调用 getInstance() 都会进行同步操作,可能会导致性能瓶颈,特别是在多线程环境中,大量调用时性能下降明显。

3. 双重检查锁定(Double-Checked Locking)

为了解决同步性能问题,可以使用"双重检查锁定"技术。只在实例为空时才加锁,避免每次调用都进行同步。

java 复制代码
public class DoubleCheckedSingleton {
    private static volatile DoubleCheckedSingleton instance;

    private DoubleCheckedSingleton() {}

    public static DoubleCheckedSingleton getInstance() {
        if (instance == null) {  // 第一次检查
            synchronized (DoubleCheckedSingleton.class) {
                if (instance == null) {  // 第二次检查
                    instance = new DoubleCheckedSingleton();
                }
            }
        }
        return instance;
    }
}
关键点
  • volatile 关键字:确保多个线程正确处理实例变量的可见性。
  • 双重检查:通过在同步块内外都进行 null 检查,避免不必要的同步操作。
优点
  • 线程安全,同时避免了不必要的同步,提高了性能。
缺点
  • 实现较为复杂,理解起来有一定的难度。

4. 饿汉式单例(Eager Initialization Singleton)

实现方式

饿汉式单例是在类加载时就创建实例,而不是在需要时才创建。这是通过直接初始化静态变量来实现的。

java 复制代码
public class EagerSingleton {
    // 类加载时就创建实例
    private static final EagerSingleton instance = new EagerSingleton();

    // 私有构造函数,防止外部实例化
    private EagerSingleton() {}

    // 提供获取实例的全局访问点
    public static EagerSingleton getInstance() {
        return instance;
    }
}
优点
  • 简单,类加载时即创建实例,线程安全。
  • 没有锁机制,性能高。
缺点
  • 不具备延迟加载特性,可能会在程序不需要该实例时就加载,浪费资源。

5. 静态内部类实现单例(Static Inner Class Singleton)

这种方式利用了Java类加载机制中的延迟加载特性。静态内部类只有在第一次被使用时才会被加载,从而实现懒加载的效果。

java 复制代码
public class InnerClassSingleton {
    private InnerClassSingleton() {}

    // 静态内部类,只有在被调用时才会装载
    private static class SingletonHolder {
        private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
    }

    public static InnerClassSingleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}
原理
  • 静态内部类 SingletonHoldergetInstance() 方法被调用时才会加载,因此实现了延迟加载。
  • 类加载机制保证了线程安全性。
优点
  • 延迟加载,线程安全,且实现简单。
  • 不需要加锁,性能优越。
缺点
  • 和饿汉式类似,加载时耗费时间,如果实例初始化很大,会带来一些性能损耗。

6. 枚举单例(Enum Singleton)

枚举类型是实现单例模式最简单且安全的方法,它不仅避免了反序列化破坏单例,还能防止反射攻击。

java 复制代码
public enum EnumSingleton {
    INSTANCE;

    public void doSomething() {
        System.out.println("Do something...");
    }
}
优点
  • 简单明了,Java语言本身提供了枚举的特性,保证了实例的唯一性。
  • 线程安全且可以防止序列化和反射攻击。
缺点
  • 枚举单例无法懒加载。

总结

单例模式在需要控制某个类的全局唯一实例时非常有用,比如数据库连接池、线程池、配置管理等场景。选择哪种实现方式取决于具体的需求:

  • 懒汉式:适合希望延迟加载的场景,但需要考虑线程安全问题。
  • 饿汉式:适合在类加载时就需要实例的场景,但无法延迟加载。
  • 双重检查锁定:适合需要线程安全且有一定性能要求的场景。
  • 静态内部类:实现了懒加载且线程安全,推荐使用。
  • 枚举:最简单且安全的实现方式,防止序列化和反射攻击,但无法延迟加载。
相关推荐
卡尔特斯3 小时前
Android Kotlin 项目代理配置【详细步骤(可选)】
android·java·kotlin
白鲸开源3 小时前
Ubuntu 22 下 DolphinScheduler 3.x 伪集群部署实录
java·ubuntu·开源
ytadpole3 小时前
Java 25 新特性 更简洁、更高效、更现代
java·后端
纪莫3 小时前
A公司一面:类加载的过程是怎么样的? 双亲委派的优点和缺点? 产生fullGC的情况有哪些? spring的动态代理有哪些?区别是什么? 如何排查CPU使用率过高?
java·java面试⑧股
JavaGuide4 小时前
JDK 25(长期支持版) 发布,新特性解读!
java·后端
用户3721574261354 小时前
Java 轻松批量替换 Word 文档文字内容
java
白鲸开源4 小时前
教你数分钟内创建并运行一个 DolphinScheduler Workflow!
java
晨米酱5 小时前
JavaScript 中"对象即函数"设计模式
前端·设计模式
Java中文社群5 小时前
有点意思!Java8后最有用新特性排行榜!
java·后端·面试
代码匠心5 小时前
从零开始学Flink:数据源
java·大数据·后端·flink