单例模式实现方法
单例模式有五种写法:懒汉、饿汉、双重检验锁、静态内部类、枚举
饿汉模式
这种方式比较常用,但容易产生垃圾对象。它基于 classloder 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。
优点:没有加锁,执行效率会提高
缺点:类加载时就初始化,浪费内存
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance() {
return instance;
}
}
双检索
双重检验锁模式(double checked locking pattern),是一种使用同步块加锁的方法。
有两次检查 instance == null,一次是在同步块外,一次是在同步块内
为什么在同步块内还要再检验一次?
- 因为可能会有多个线程一起进入同步块外的 if,如果在同步块内不进行二次检验的话就会生成多个实例了
volatile来保证instance的顺序
public class Singleton {
private volatile static Singleton instance;
private Singleton(){}
public static Singleton getInstance() {
if(instance == null) {
synchronized(Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
在getInstance()方法上同步有优势还是仅同步必要的块更优优势?
因为锁定仅仅在创建实例时才有意义,然后其他时候实例仅仅是只读访问的,因此只同步必要的块的性能更优,并且是更好的选择
锁有开销