作为对象的创建模式,单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类。
单例模式的特点
单例类只能有一个实例(static变量保证在内存中只有一个拷贝,当遭遇多线程情况下并不能保证返回的是同一个实例,前者的实例会被后者覆盖)
单例类必须自己创建之自己唯一的实例(构造器私有化,不能被外部实例化)
单例类必须给所有其他对象提供这一实例(需要线程安全的的系统提供该单例对象)
单例的结构
饿汉式单例类
空间换时间
public class EagerSingleton {
//在静态static修饰的同时,加上final修饰不可变,保证该变量不会被修改
private static final EagerSingleton instance = new EagerSingleton();
//私有构造函数,防止外部对其实例化
private EagerSingleton() {}
//共有静态方法,提供对外的接口调用
public static EagerSingleton getInstance(){
return instance;
}
}
懒汉式单例模式
时间换空间
public class LazySingleton {
//不能被final修饰,一旦被初始化就不能在改变其引用了
private static LazySingleton instance = null;
private LazySingleton() {}
// 缺点: 锁颗粒太大,当多线程获取实例的时候,每次都需要阻塞等待
public synchronized static LazySingleton getInstance(){
if(instance == null){
instance = new LazySingleton();
}
return instance;
}
}
双重检测的懒汉式单例模式
效率已经很高了
public class Singleton {
private volatile static Singleton instance = null; //看到很多博客上都加volatile关键字来保证内存可见性,以及防止指令重排序,不过笔者觉得没必要,因为synchronized 已经可以确保原子性,可见性
private Singleton() {}
public static Singleton getInstance(){
// 相比于前者的锁方法每次获取实例都需要阻塞,双重检测只需在第一次多线程阻塞完成之后,之后获取实例通过第一重检测就能判断出该实例,第一重检测不需要加锁
if(instance == null){ //第一重检测,在锁外边先检测实例是否为空,可能有多个线程同时通过第一重检测
// 多线程阻塞等待,
synchronized (Singleton.class) {
if(instance == null){//第二重检测,在锁里面再检测一遍实例是否为空,有且只能有一个实例通过该检测
instance = new Singleton(); // new一个对象,分三步,分配内存空间,初始化,赋值
}
}
}
return instance;
}
}
类级内部类模式
public class InnerSingleton {
private InnerSingleton(){}
private static class singletonHolder{//只有当调用类级内部类的时候,才会加载静态变量,JVM在静态初始化器初始化数据时,保证线程安全
private static final InnerSingleton instance = new InnerSingleton();
}
public static InnerSingleton getInstance(){
return singletonHolder.instance;
}
}
浙公网安备 33010602011771号