V1版:2024年6月25日 13点更新。
单例模式
记忆技巧:
- 一个人只能有一张身份证。
- 一所学校只能有一个校长室。
重点理解“单”字。
基础概念:
- 能够确保系统中类只有一个实例。
- 构造函数必须私有化【private 类名() { }】。
实现方式
- 懒汉式(线程不安全)
- 懒汉式(线程安全)
- 饿汉式
- 双重检查锁定
- 静态内部类
- 枚举
懒汉式(线程不安全):
public class Singleton { // 静态实例变量,初始时为null private static Singleton instance; // 私有构造函数,防止外部实例化 private Singleton() {} // 获取实例的静态方法 public static Singleton getInstance() { // 首次访问时,如果实例为null,创建新的实例 if (instance == null) { instance = new Singleton(); } // 返回实例 return instance; } }
懒汉式(线程安全):
public class Singleton { // 静态实例变量,初始时为null private static Singleton instance; // 私有构造函数 private Singleton() {} // 获取实例的静态方法,添加同步锁确保线程安全 public static synchronized Singleton getInstance() { // 首次访问时,如果实例为null,创建新的实例 if (instance == null) { instance = new Singleton(); } // 返回实例 return instance; } }
饿汉式:
public class Singleton { // 在类加载时就创建实例 private static Singleton instance = new Singleton(); // 私有构造函数 private Singleton() {} // 获取实例的静态方法 public static Singleton getInstance() { // 直接返回已创建的实例 return instance; } }
双重检查锁定:
public class Singleton { // 使用volatile关键字保证多线程环境下的可见性和禁止指令重排序 private static volatile Singleton instance; // 私有构造函数 private Singleton() {} // 获取实例的静态方法 public static Singleton getInstance() { // 第一次检查,避免不必要的同步 if (instance == null) { // 同步块,确保线程安全 synchronized (Singleton.class) { // 第二次检查,防止多次实例化 if (instance == null) { instance = new Singleton(); } } } // 返回实例 return instance; } }
静态内部类:
public class Singleton { // 私有构造函数 private Singleton() {} // 静态内部类,包含一个静态属性Singleton private static class SingletonHolder { // 在内部类被加载和初始化时,创建单例实例 private static final Singleton INSTANCE = new Singleton(); } // 获取实例的静态方法 public static Singleton getInstance() { // 返回内部类的静态属性 return SingletonHolder.INSTANCE; } }
枚举:
public enum Singleton { // 定义一个枚举的元素,它就代表了Singleton的一个实例 INSTANCE; }
注意事项
线程安全性: 如果应用程序可能会在多线程环境下使用单例对象,需要确保实现线程安全的单例模式。常见的线程安全实现包括使用双重检查锁定(double-checked locking)、静态内部类等方式来保证在多线程环境下只创建一个实例,并且保证获取实例的高效性和安全性。
应用案例
- 日志记录器:确保应用程序中只有一个日志记录器实例,用于统一管理和记录日志信息。
- 配置管理器:管理应用程序的配置信息,确保全局唯一的配置管理器实例。
- 数据库连接池:管理数据库连接的单例,确保在整个应用程序中只有一个连接池实例。
- 线程池:确保在应用程序中只有一个线程池实例来统一管理线程资源。
- 计数器:跟踪应用程序中某种资源或事件的数量,确保全局唯一的计数器实例。
面试题目
问题: 如何实现线程安全的单例模式?
答案:使用双重检查锁定、静态内部类和枚举实现线程安全的单例模式。