单例模式是开发中使用频率极高的一个模式,比如Spring的bean默认就是单例的。
概念
单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
单例模式有一下特点:
- 单例类只有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
实现方式
一般意义上来讲单例模式有两种实现方式,懒汉模式和饿汉模式。
饿汉模式
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
类加载自动初始化对象,不是lazy init ,系统启动的压力增大。
懒汉模式
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
典型的懒汉模式,在需要初始化使用单例对象时才决定是否初始化,减轻了启动的负担,但是无法在多线程环境下运行,可能会导致产生多个实例。
改进版的懒汉模式
为了解决懒汉模式在多线程情况的问题,我们使用synchronized关键词同步代码,不就可以避免了同步问题了吗?
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
这种改良方式的确会解决多线程的问题,但同时synchronized会导致每次方法调用时锁住Singleton
实例一次,性能消耗太大。而且instance一旦初始化后,同步锁更加拖累效率。
双重锁定的懒汉模式
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
这种模式避免了域在被初始化之火访问这个域的锁定开销。这种模式的基本思想是:
第一次检查时看看有没有锁定,看一看这个域是否已被初始化了;第二次检查时有锁定,只有当第二次检查时表明这个域没有被初始化,才开始进行初始化。
这种模式既规避了延迟加载也避免了重复加锁带来的额外性能消耗。
静态内部类
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
当getInstance方法第一次调用时,第一次读取SingletonHolder.INSTANCE,导致SingletonHolder类得到初始化,这种方式并没有使用任何线程同步的机制,就保证了一次访问域代码。延迟初始化没有增加访问成本,这个方法值得推荐。
结论
5种方式,相对比较推荐第4种和第5种,做到了延迟加载,也没有引入额外的问题和性能开销。