Singleton pattern
限定类对象只有一个实例
核心原理是将构造函数私有化,并且通过静态方法获取一个唯一的实例,在这个过程中保证线程安全。
饿汉模式
实例在类加载初始化的时候就由<clinit>函数创建
public final class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
缺点:饿汉模式初始化的过早,如果仅仅调用了单例类的静态函数,也会进行初始化
懒汉模式
延迟加载:在使用时才初始化,效率高,第一次加载反应稍慢
通过 double-checked locking 双重检查锁定,实现线程安全
public final class Singleton {
private static volatile Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
volatile的作用:保证变量可见性和禁止指令重新排序
在这里主要是避免指令重排,否则Singleton
实例分配对象后,可能直接将instance
指向对应的内存空间,而构造函数还在初始化成员字段,此时另一个线程就会直接使用尚未初始化完成的单例对象instance
,可能产生错误
静态内部类
线程安全,延迟了实例化,只有在调用getInstance时才会实例化
public final class Singleton{
private Singleton(){}
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder{
private static final Singleton INSTANCE = new Singleton();
}
}
静态内部类的加载不需要依附外部类,在使用时才加载。不过在加载静态内部类的过程中也会加载外部类
枚举单例
写法简单,通过Singleton.INSTANCE.getInstance()
获取单例
class Resource{
}
public enum Singleton{
INSTANCE;
private Resource instance;
Singleton() {
instance = new Resource();
}
public Resource getInstance() {
return instance;
}
}
- enum没有public的构造器,防止外部的额外构造,恰好和单例模式吻合
- 用枚举实现单例,类似于饿汉模式,没有实现延迟实例化
- 枚举的反序列化不会生产新的实例
容器类
将多种单例类型注入到一个统一管理的容器中,在使用时根据key获取对应类型的对象。这种方式可以管理多种类型的单例,使用统一的接口进行获取操作
public class SingletonManager {
private static Map<String, Object> objMap = new HashMap<String,Object>();
private Singleton() {
}
public static void registerService(String key, Objectinstance) {
if (!objMap.containsKey(key) ) {
objMap.put(key, instance) ;
}
}
public static ObjectgetService(String key) {
return objMap.get(key) ;
}
}