单例模式(Singleton Pattern)是一种常用的软件设计模式,它确保一个类只有一个实例,并提供一个全局访问点。这种模式在需要全局状态或者希望整个应用程序中某个对象只有一个实例时非常有用。
单例模式的关键特点包括:
- 唯一实例:单例类通过私有构造函数和静态实例变量确保只有一个实例被创建。
- 全局访问点:单例类提供一个公共的静态方法,允许其他对象获取到这个唯一实例的引用。
- 延迟初始化:单例实例可以在第一次使用时创建,也可以在类加载时立即创建。延迟初始化有助于节省资源,因为它允许系统在需要时才创建实例。
- 线程安全:在多线程环境中,单例模式需要确保线程安全,避免多个线程同时创建多个实例。
实现方案的话,我这里推荐三种:
1、枚举(推荐)
java
public enum Singleton {
INSTANCE;
public void doSomething(String str) {
System.out.println(str);
}
}
《Effective Java》作者推荐的⼀种单例实现⽅式,简单⾼效,⽆需加锁,线程安全,可以避免通过 反射破坏枚举单例。
2、静态内部类(推荐)
java
public class Singleton {
// 私有化构造⽅法
private Singleton() {
}
// 对外提供获取实例的公共⽅法
public static Singleton getInstance() {
return SingletonInner.INSTANCE;
}
// 定义静态内部类
private static class SingletonInner{
private final static Singleton INSTANCE = new Singleton();
}
}
当外部类Singleton被加载的时候,并不会创建静态内部类SingletonInner的实例对象。只有当调 ⽤getInstance()⽅法时, SingletonInner才会被加载,这个时候才会创建单例对象INSTANCE 。 INSTANCE的唯⼀性、创建过程的线程安全性,都由 JVM 来保证。 这种⽅式同样简单⾼效,⽆需加锁,线程安全,并且⽀持延时加载。
3、双重校验锁
java
public class Singleton {
private volatile static Singleton uniqueInstance;
// 私有化构造⽅法
private Singleton() {
}
public static Singleton getUniqueInstance() {
//先判断对象是否已经实例过,没有实例化过才进⼊加锁代码
if (uniqueInstance == null) {
//类对象加锁
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
uniqueInstance采⽤volatile关键字修饰也是很有必要的,uniqueInstance = new Singleton();这段 代码其实是分为三步执⾏: 1. 为uniqueInstance分配内存空间 2. 初始化uniqueInstance 3. 将uniqueInstance指向分配的内存地址 但是由于 JVM 具有指令重排的特性,执⾏顺序有可能变成 1->3->2。指令重排在单线程环境下不会 出现问题,但是在多线程环境下会导致⼀个线程获得还没有初始化的实例。例如,线程 T1 执⾏了 1 和 3,此时 T2 调⽤getUniqueInstance () 后发现uniqueInstance不为空,因此返回uniqueInstance ,但此时uniqueInstance还未被初始化。
这种⽅式实现起来较麻烦,但同样线程安全,⽀持延时加载。