单利模式有很多种实现方式,每一种方式都有自己合适的场合。常用的单利有:懒汉式、饿汉式、线程安全式、双重检查式和登记式。实现单利最重要的一点是构造方法是private的,这使得实例不能被外界所创建。
懒汉式
这是最简单的一种单利模式,但是带来的问题是线程不安全。这里所谓的不安全是指在创建第一个实例时是线程不安全的。可以看到,当我们初次使用时才会创建实例,所以叫懒汉式。
public class SimpleSingleton {
private static SimpleSingleton simpleSingleton;
//必须为private,这是实例独一无二的保证
private SimpleSingleton(){
}
public static SimpleSingleton getInstance(){
//如果不存在则实例一个
if(simpleSingleton==null){
//创建第一个实例时是线程不安全的
simpleSingleton = new SimpleSingleton();
}
return simpleSingleton;
}
}
线程安全式
为了解决线程安全的问题,我们只需要在获得实例的方法上同步即可,但是这就会造成程序性能下降。
public class SafeSingleton {
private static SafeSingleton safeSingleton;
// 必须为private,这是实例独一无二的保证
private SafeSingleton() {
}
//保证线程安全
public static synchronized SafeSingleton getInstance() {
// 如果不存在则实例一个
if (safeSingleton == null) {
safeSingleton = new SafeSingleton();
}
return safeSingleton;
}
}
饿汉式
可以看到,在JVM加载这个类时,就会创建实例。JVM能保证在任何线程访问到eagerSingleton实例之前将其创建完成。
public class EagerSingleton {
//JVM加载这个类时就创建
private static EagerSingleton eagerSingleton = new EagerSingleton();
private EagerSingleton(){};
public static EagerSingleton getInstance(){
return eagerSingleton;
}
}
双重检查
线程安全式中,同步了整个getInstance方法才保证了线程安全,会浪费很多性能。于是我们可以使用双重检查式,它将只在初次创建实例的时候实现同步。(了解锁优化的同学可以理解为减少锁的颗粒度)
public class DoubleCheckSingleton {
private volatile static DoubleCheckSingleton doubleCheckSingleton;
private DoubleCheckSingleton() {
};
public static DoubleCheckSingleton getInstance() {
//这里没有加synchronized
if (doubleCheckSingleton == null) {
//很多线程都能到达这里
synchronized (DoubleCheckSingleton.class) {
//有可能已经有线程更早一步创建了单利
if (doubleCheckSingleton == null)
doubleCheckSingleton = new DoubleCheckSingleton();
}
}
return doubleCheckSingleton;
}
}
登记式
虽然和饿汉式一样使用了静态域,但是此种方式的实例加载是Lazy的。
public class StaticSingleton {
// 利用了 classloder 机制来保证初始化 instance 时只有一个线程
private static class SingletonHolder {
private static final StaticSingleton INSTANCE = new StaticSingleton();
}
private StaticSingleton() {
}
public static final StaticSingleton getInstance() {
return SingletonHolder.INSTANCE;
}
}