单例模式介绍
单例模式是指一个类只允许创建一个实例,并提供一个全局访问点。它是一种常用的设计模式,通常用于管理共享资源、配置信息等。
单例模式的基本思想很简单:将类的构造函数定义为私有的,这样外部就无法通过构造函数来创建该类的实例;而通过一种特殊的方法来获取该类的唯一实例。这种特殊的方法通常被称为“getInstance”,它返回该类的唯一实例。由于该类的实例仅有一个,并且由该类自行创建,所以它可以保证全局唯一性,并且可以避免对资源的多重占用。
在实际应用中,单例模式有许多变体,例如懒汉式单例、饿汉式单例、双重检查锁单例、静态内部类单例等。下面我们来逐个介绍它们。
饿汉式单例模式
饿汉式单例模式是最简单的一种单例模式,它在类加载时就创建了实例,因此在程序运行期间不会再次创建实例,保证了全局唯一性。
public class Singleton { // 在类加载时就创建实例 private static Singleton instance = new Singleton();
// 将构造方法私有化,禁止外部通过构造方法创建实例
private Singleton() {}
// 提供一个公共的访问方法,用于获取该类的唯一实例
public static Singleton getInstance() {
return instance;
}
}
懒汉式单例模式
懒汉式单例模式是在第一次使用时才进行实例化,可以节省内存空间。但是它需要考虑线程安全问题,否则可能出现多线程下创建多个实例的情况。
public class Singleton {
// 用 volatile 关键字确保 instance 在多线程下的可见性
private static volatile Singleton instance = null;
// 将构造方法私有化,禁止外部通过构造方法创建实例
private Singleton() {}
// 提供一个公共的访问方法,用于获取该类的唯一实例
public static Singleton getInstance() {
if (instance == null) { // 如果 instance 为空,就进行实例化
synchronized (Singleton.class) { // 保证多线程下只有一个线程进行实例化
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
双重检查锁单例模式
双重检查锁单例模式是在懒汉式单例模式的基础上,加入了同步锁来保证线程安全,并且加入了第二次判断,避免多线程下创建多个实例。
public class Singleton {
// 用 volatile 关键字确保 instance 在多线程下的可见性
private static volatile Singleton instance = null;
// 将构造方法私有化,禁止外部通过构造方法创建实例
private Singleton() {}
// 提供一个公共的访问方法,用于获取该类的唯一实例
public static Singleton getInstance() {
if (instance == null) { // 如果 instance 为空,就进行实例化
synchronized (Singleton.class) { // 保证多线程下只有一个线程进行实例化
if (instance == null) { // 第二次判断,避免多线程下创建多个实例
instance = new Singleton();
}
}
}
return instance;
}
}
静态内部类单例模式
静态内部类单例模式是在类加载时会创建一个静态内部类,调用 getInstance 方法时才会去加载该内部类并初始化 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;
}
}
上面介绍的是四种常见的单例模式,每种单例模式都有其优缺点,根据实际需求进行选择。例如在单线程环境下,可以使用简单的饿汉式单例模式;而在多线程环境下,可以使用双重检查锁或静态内部类单例模式。