设计模式之单例模式:一个类就只有一个实例

1. 介绍

单例模式(Singleton Pattern)是 Java 中最简单最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

主要用来解决频繁创建和销毁全局使用的类实例的问题。当需要控制实例数目,节省系统资源时使用。

  • 优点

    • 内存中只有一个实例,减少内存开销,尤其是频繁创建和销毁实例时(如管理学院首页页面缓存)。

    • 避免资源的多重占用(如写文件操作)。

  • 缺点

    • 没有接口,不能继承。

    • 与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心实例化方式。

2. 实现及相关代码

在 Java 中,单例模式(Singleton Pattern)的常见实现方式有 饿汉式(Eager Initialization)懒汉式(Lazy Initialization) ,其中懒汉式又分为 线程不安全线程安全(双重检查锁) 两种。

2.1 饿汉式(Eager Initialization)

特点:类加载时就初始化实例,线程安全,但可能浪费资源(如果实例未被使用)。

java 复制代码
public class EagerSingleton {

    // 类加载时就初始化实例
    private static final EagerSingleton INSTANCE = new EagerSingleton();

    // 私有构造方法,防止外部 new 实例
    private EagerSingleton() {}

    // 提供全局访问点
    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
}

优点

  • 线程安全(JVM 保证类加载时初始化)

  • 实现简单

缺点

  • 如果实例未被使用,会造成资源浪费

2.2 懒汉式(Lazy Initialization)

2.2.1 线程不安全版本(不推荐)
java 复制代码
public class LazySingleton {
    private static LazySingleton instance;

    private LazySingleton() {}

    public static LazySingleton getInstance() {
        if (instance == null) {
            // 多线程下可能创建多个实例
            instance = new LazySingleton();
        }
        return instance;
    }
}

缺点:多线程环境下可能创建多个实例,不推荐使用。

2.2.2 线程安全版本(synchronized 方法)
java 复制代码
public class LazySingleton {
    private static LazySingleton instance;

    private LazySingleton() {}

    // 加 synchronized 保证线程安全,但性能较差
    public static synchronized LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

缺点 :每次调用 getInstance() 都要加锁,性能较差。

2.2.3 双重检查锁(Double-Checked Locking,推荐)
java 复制代码
public class LazySingleton {
    // 使用 volatile 防止指令重排序
    private static volatile LazySingleton instance;

    private LazySingleton() {}

    public static LazySingleton getInstance() {
        if (instance == null) { // 第一次检查
            synchronized (LazySingleton.class) { // 加锁
                if (instance == null) { // 第二次检查
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}

优点

  • 线程安全

  • 只有第一次初始化时加锁,后续调用无锁,性能较好

注意volatile 关键字防止 JVM 指令重排序,避免返回未初始化的实例。

2.3 静态内部类实现(推荐)

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

    // 静态内部类在第一次使用时加载,保证线程安全
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

优点

  • 线程安全(JVM 保证类加载时初始化)

  • 懒加载(只有调用 getInstance() 时才初始化)

  • 实现简单,无锁

3. 总结

实现方式 线程安全 懒加载 性能 适用场景
饿汉式 ⭐⭐⭐ 单例对象占用资源少,且一定会使用
懒汉式(synchronized) 不推荐,性能差
双重检查锁(DCL) ⭐⭐⭐ 推荐,兼顾线程安全和性能
静态内部类 ⭐⭐⭐ 推荐,无锁,代码简洁

推荐

  • 如果单例对象占用资源少,且一定会使用 → 饿汉式

  • 如果需要懒加载,且对性能有要求 → 双重检查锁静态内部类

相关推荐
张小姐的猫17 小时前
【Linux】网络编程 —— HTTP协议(上)
linux·运维·服务器·网络·http·单例模式·策略模式
杨充20 小时前
10.可测试性实战设计
设计模式·开源·代码规范
杨充20 小时前
9.重构十二式的实战
设计模式·开源·代码规范
杨充20 小时前
6.设计原则的全景图
设计模式·开源·全栈
杨充20 小时前
2.面向对象的特性
设计模式
杨充20 小时前
7.SOLID原则案例汇
设计模式·开源·全栈
杨充20 小时前
8.反模式与坏味道
设计模式·开源·代码规范
杨充20 小时前
3.接口vs抽象类比较
设计模式
咖啡八杯1 天前
文法、BNF与AST
java·设计模式·解释器模式·ast·文法