懒汉式:
public class Singleton {
private static Singleton instance;
private Singleton (){} //不让外部new
public static synchronized Singleton getInstance (){ //去掉synchronized是线程不安全的
if (instance == null) { //1:A线程执行
instance = new Singleton(); //2:B线程执行
}
return instance;
}
}
“这个人比较懒,等着你用的时候才去实例化”,延迟加载。
优点:第一次调用才初始化,避免内存浪费
缺点:每次获取实例都要加锁,耗费资源,其实只要实例已生成,以后就不需要再锁了
饿汉式:
public class Singleton {
private static Singleton instance = new Singleton(); //声明静态私有类变量,且立即实例化,
//保证实例化一次
private Singleton(){} //构造方法,防止外部实例化,通过反射是可以实例化的,不考虑此种情况
public static Singleton getInstance() {
return instance;
}
}
“因为饿所以要立刻吃饭,刻不容缓”,在定义类的静态私有变量同时进行实例化
优点:线程安全,没有加锁,执行效率会更高
缺点:类加载时就初始化,浪费内存。
双重检查加锁模式:
public class Singleton {
private volatile static Singleton singleton; // 注意:加了volatile关键字
private Singleton(){}
public static Singleton getInstance(){
if(singleton == null){ //第一次检查
synchronized(Singleton.class){ //加锁
if(singleton == null){ //第二次检查
singleton = new Singleton(); //
}
}
}
return singleton;
}
}
这种模式可能会引起空指针异常,new 一个对象需要经历:分配内存空间-初始化-引用赋值(指向对象的地址)过程,而且java中存在cpu指令重排序,因此在多线程环境下线程一执行 可能执行new对象:分配内存空间-引用赋值-初始化,当执行到引用赋值时,线程二会看到instance!=null,但这时,对象并未完成初始化,对象是空的,直接使用会空指针异常。因此这种方式也是线程不安全的。
volatile 在这的作用就是禁止指令重排序,从而使对象的创建严格按照分配内存空间-初始化-引用赋值(指向对象的地址)过程,实现了线程安全。
静态内部类:
public class Singleton {
private Singleton(){}
public static Singleton getInstance(){
return InnerClassSingleton.singleton;
}
private static class InnerClassSingleton{
protected static final Singleton singleton = new Singleton();
}
}
优点:既避免了同步带来的性能损耗,又能够延迟加载
枚举:
public enum Singleton {
INSTANCE;
public void init(){
System.out.println("资源初始化。。");
}
}
天然线程安全,可防止反射生成实例