单例模式:是一种常用的设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候,整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,显然,这种方式简化了在复杂环境下的配置管理。
常见单例模式:懒汉式,饿汉式等。
懒汉式式单例(Singleton)
优点:没有加锁,执行效率会提高。 缺点:类加载时就初始化,浪费内存。
public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){} //构造方法私有化 public static Singleton getInstance() { return instance; } }
不安全饿汉式(UnsafeLazySingleton )
严格意义上它并不算单例模式。这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。
/**
* @author buyi
* 不安全的懒加载单例
*
* 不安全原因:
* 1.假设A线程和B线程同时开始执行代码2,会new出两个对象
* 1.假设A线程执行代码1的同时,B线程执行代码2。此时,线程A可能会看到instance引用的对象还没有完成初始化
*/
public class UnsafeLazySingleton {
private static Object instance;
public Object getSingleton() {
if (null == instance) { //代码1
instance = new Object(); //代码2
}
return instance;
}
}
安全饿汉式(SafeLazySingleton)
具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。优点:第一次调用才初始化,避免内存浪费。缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
/**
* @author buyi
* 安全的懒加载单例
*
* 待优化原因:
* getSingleton()方法做了同步处理,synchronized将导致性能开销。如果getSingleton()方 法被多个线程频繁的调用,将会导致程序执行性能的下降。
* 反之,如果getSingleton()方法不会被 多个线程频繁的调用,那么这个延迟初始化方案将能提供令人满意的性能。
*/
public class SafeLazySingleton {
private static Object instance;
public synchronized Object getSingleton() {
if (null == instance) { //代码1
instance = new Object(); //代码2
}
return instance;
}
}
基于双重校验锁(DoubleCheckLazySingleton)
/**
* @author buyi
*
* 万事大吉?
* 在线程执行到##时,代码读取到instance不为null时,instance引用的对象有可能还没有完成初始化
*/
public class DoubleCheckLazySingleton {
private static Object instance;
public Object getSingleton() {
if (null == instance) { //第一次检查 ##
synchronized (DoubleCheckLazySingleton.class) { //加锁
if (null == instance) { //第二次检查
instance = new Object(); //实例化
}
}
}
return instance;
}
}
问题根源:在于实例化new Object()时候,可以分解为三步骤

此时可能发生指令重排序2和3之间
那么线程A和线程B执行时序图可能为:
A2和A3虽然重排序了,但Java内存模型的intra-thread semantics将确保A2一定会排在A4前面执行。因此,线程A的intra-thread semantics没有改变,但A2和A3的重排序,将导致线程B在B1处判断出instance不为空,线程B接下来将访问instance引用的对象。此时,线程B将会访问到一个还未初始化的对象。
双重检查锁升级版(DoubleCheckSafeLazySingleton)
基于双重检查锁和volatile(防止指令重排序)的单例模式
/** * @author buyi * * 优化版基于双重检查锁和volatile的单例 */ public class DoubleCheckSafeLazySingleton { private DoubleCheckSafeLazySingleton(){}//构造方法私有化 private volatile static Object instance; public Object getSingleton() {
if (null == instance) {
synchronized (DoubleCheckSafeLazySingleton.class){
if (null == instance) {
instance = new Object(); }
} }
return instance;
} }
静态内部类单例(Singleton)
SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。在延迟加载方面比懒汉式更好。
public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }