优点:代码简单易读,由JVM类加载机制保证了线程安全,无需业务方关心。
缺点:无法做到延迟创建对象,在加载class的时候就会创建对象。
优点:代码简单易读。
缺点:在多线程情况下非现场安全,可能出现创建多个实例。
优点:代码简单易读,线程安全。
缺点:每次调用时,都需要线程同步,性能较差。
优点:由JVM保证线程安全,不需要业务方关心。
缺点:第一次访问可能较慢,他需要加载内部类这样可能占用更多的code区域的内存。
枚举在单例上的使用较其他有点不太一样,使用示例如下:
Singleton.INSTANCE.doSomeThing();
优点:由JVM保证了线程安全,代码简洁。
缺点:代码不易理解。
Joshua Bloch大神较为推崇这种写法,他在《Effective Java》中明确表达过下面观点:
使用枚举实现单例的方法虽然还没有广泛采用,但是单元素的枚举类型已经成为实现Singleton的最佳方法。
优点:线程安全
缺点:每次访问对象都需要判null,并且volatile导致每次都会访问主内存,性能相比较而言较差。 该单例模式较容易出错,特别是volatile关键字要加,具体原因可以参考: Java面试官最喜欢问的关键字-volatile
线程安全 |
性能 |
可读性 |
延迟加载 |
推荐使用不 |
|
---|---|---|---|---|---|
恶汉 |
是 |
好 |
好 |
否 |
推荐 |
懒汉(非线程安全) |
否 |
好 |
好 |
是 |
不推荐 |
懒汉(线程安全) |
是 |
差 |
好 |
是 |
不推荐 |
内部类 |
是 |
好 |
一般 |
是 |
一般 |
枚举 |
是 |
好 |
一般 |
? |
推荐 |
双重校验锁 |
是 |
一般 |
一般 |
是 |
一般 |
具体选择哪种方式肯定是具体场景具体分析;但是一般情况下,我是比较推荐恶汉和枚举方式。
大家如果阅读过JDK源码可能就会注意到,JDK主要采用的恶汉和懒汉模式,基本上不会采用枚举、双重校验锁;我猜测可能的原因双重双重校验锁再JDK1.5版本之前有问题并且枚举是在JDK1.5版本才引用的,导致历史上很多JDK在实现单例模式没有那么多选择。
其他很多开源项目在选择单例上就较为丰富,基本上每种单例都可能遇到,比如在Hystrix就遇到了恶汉模式(HystrixTimer)和内部类(HystrixPlugins) 。