饿汉式(常用)
public class EagerSingleton {
private static EagerSingleton instance = new EagerSingleton();
/**
* 私有默认构造子
*/
private EagerSingleton(){}
/**
* 静态工厂方法
*/
public static EagerSingleton getInstance(){
return instance;
}
}
懒汉式
public class LazySingleton {
private static LazySingleton instance = null;
/**
* 私有默认构造子
*/
private LazySingleton(){}
/**
* 静态工厂方法
*/
public static synchronized LazySingleton getInstance(){
if(instance == null){
instance = new LazySingleton();
}
return instance;
}
}
简单总结一下这样的写法:
- 提供一个全局静态的
getIns
方法,使得易于使用. - 延迟Singleton的实例化,节省资源(所谓的懒汉式).
- 缺点是线程不安全.当多个线程同时进入
if (null == ins) {}
的时候就会创建多个实例.
饿汉式与之前提到的懒汉式不同,它在我们调用getIns之前就实例化了(在类加载的时候就实例化了),所以不是一个懒加载,这样就有几个缺点:
- 延长了类加载时间
- 如果没用到这个类,就浪费了资源(加载了但是没用它)
- 不能传递参数(很显然适用的场景会减少)
性能进阶
接下去介绍一种更优秀的,线程安全的单例写法---双重检查锁模式(double check locking pattern)
public class DoubleCheck {
private volatile static DoubleCheck ins;
private DoubleCheck() {}
public static DoubleCheck getIns() {
if (null==ins){ //检查
synchronized (DoubleCheck.class){
if (null == ins) { //又检查一次
ins = new DoubleCheck();
}
}
}
return ins;
}
}
注意这里的ins用了volatile
关键字来修饰,为什么呢?
因为执行ins = new DoubleCheck()
做了很多事情:
- 给ins分配内存
- 调用构造函数来初始化成员变量(可能会很久)
- 将ins对象指向分配的内存空间(执行完这步 ins才不为null)
静态内部类
静态内部类原理同上,另外虽然它看上去有点恶汉式,但是与之前的恶汉有点不同,它在类Singleton
加载完毕后并没有实例化,而是当调用getIns
去加载Holder的时候才会实例化,静态内部类的方式把实例化延迟到了内部类的加载中去了!所以它比饿汉式更优秀!(偷偷告诉你Effective Java中也推荐这个方式)
例子:
public class Singleton {
private static class Holder{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getIns(){
return Holder.INSTANCE;
}
}
推荐双重检查锁和静态内部类