单例模式是常用的一种设计模式,线程安全的单例模式一般有三种写法,其中双重检查模式是最复杂的,也是不推荐的。
那有没有更好的推荐方式呢?答案是肯定的。请继续往下看。
1. 不使用同步锁
饱汉式
public class Singleton {
//直接初始化
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
优点:这种方式的性能是非常好的,只是简单返回instance,没有做其他任何锁操作,所以在并行程序中,会有良好表现。
缺点:但是有点明显的不足之处是 Singleton 实例在何时被创建并不受控制,对于静态成员变量instance,会在类第一次初始化的时候被创建,但是这并不一定是getInstance() 方法第一次调用的时候。
2. 使用同步锁
懒汉式
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
优点:充分利用了延迟加载,只在真正需要的时候创建对象。(只在第一次调用时创建对象)
缺点:为了防止对象被多次创建,不得不使用synchronized进行方法同步。并发环境下,竞争激烈的场合对性能可能产生一定的影响。
3. 双重检查模式
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if(instance == null) {
synchronized(Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
优点:这种方式在第二种的基础上做了改进,调用 getInstance() 方法的时候不需要每次都锁类对象。如果 instance 实例已经创建,则直接返回。
缺点:在JDK1.5之后才能保证正确性,比较丑陋复杂,这里不推荐,推荐如下第四种方式。
4. 利用虚拟机的类初始化机制
这里介绍下推荐使用的一种,这种方式结合了第一种不需要同步锁和第二种延迟加载的优点。
public class Singleton {
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
//内部静态私有化类
private static class SingletonHolder {
private static Singleton instance = new Singleton();
}
}
这种方式没有锁,在高并发环境下性能更好;延迟加载,只在第一次调用时被创建。
这种方式巧妙地利用了内部类和类的初始化方式,内部类被申明为 private ,这使得我们不可能在外部访问并初始化它。只可能在 getInstance() 方法内部对 SingletonHolder 类进行初始化。