Skip to content

单例模式

单例模式(Singleton Pattern)属于创建型模式的一种。它的核心目的是控制类的实例化过程,确保在任何情况下,对于特定类来说,在整个应用程序中最多只有一个实例存在,并提供一个全局访问点来获取这个唯一的实例。这样做的主要好处是能够有效地控制资源的使用,特别是那些需要频繁创建和销毁的对象,比如数据库连接池、线程池、缓存服务等。

单例模式的特点

  1. 唯一性:确保系统中该类只有一个实例。
  2. 全局访问点:提供一个全局访问点来获取这个实例,通常通过静态方法实现。
  3. 延迟初始化:根据实现方式的不同,实例化可以在类加载时完成(饿汉式),或者首次请求实例时完成(懒汉式)。
  4. 线程安全:在多线程环境下,必须确保单例的创建过程是线程安全的。

实现单例模式的常见方法

提示

  • 一个私有构造函数
  • 一个私有静态变量
  • 一个公有静态函数

1. 饿汉式(静态常量)

在类加载时就完成了初始化,避免了线程同步问题,但可能会造成资源浪费。

java
public class Singleton {
    private final static Singleton INSTANCE = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

2. 懒汉式(线程不安全)

首次使用时才创建实例,但原始实现线程不安全。

java
public class Singleton {
    private static Singleton instance;
    private Singleton() {}
    public static Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

3. 懒汉式(线程安全,同步方法)

通过在getInstance方法上加锁保证线程安全,但每次访问都会同步,影响性能。

java
public class Singleton {
    private static Singleton instance;
    private Singleton() {}
    public synchronized static Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

4. 双重检查锁定(DCL, Double-Checked Locking)

结合懒汉式和性能优化,既实现了延迟加载,又减少了锁的开销。

java
public class Singleton {
    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;
    }
}

5. 静态内部类

利用Java类加载机制保证线程安全,且实现懒加载。

java
public class Singleton {
    private Singleton() {}
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

6.枚举

使用枚举来实现单例是一种既简单又线程安全的方法,同时还能防止反序列化破坏单例的特性。

java
public enum Singleton {
    INSTANCE;

    // 可以在这里添加其他实例方法或属性

    public void someBusinessMethod() {
        // 实际业务逻辑
    }
}

使用枚举实现单例的优点:

  1. 线程安全:Java保证了枚举实例的创建是线程安全的,因此无需担心多线程环境下的实例化问题。
  2. 简洁:实现代码非常简洁,易于理解和维护。
  3. 防止反射攻击:由于枚举类型的特殊性,它能天然防止通过反射来实例化多个对象。
  4. 防止序列化破坏:如果实现Serializable接口,枚举的序列化和反序列化机制会保证单例特性不会被破坏。

如何使用:

访问单例实例就像访问枚举值一样简单:

java
Singleton singleton = Singleton.INSTANCE;
singleton.someBusinessMethod();

虽然枚举实现单例的方式可能不是最直观的,但它提供了高度的线程安全性和简洁性,特别是在对并发控制有严格要求的场景下,是一个值得推荐的实现方式。

应用场景

  • 当需要控制资源的共享,例如数据库连接池。
  • 当对象需要被全局访问,如日志记录器、配置管理器。
  • 当对象的创建成本较高,如复杂的初始化过程,需要保证只创建一次。

注意事项

  • 单例模式可能引入全局状态,增加模块间的耦合,需谨慎使用。
  • 反射和序列化可能破坏单例,需要额外处理来防御。
  • 在并发编程中,确保单例的线程安全性是非常重要的。

Released under the MIT License.