一、简介
单例模式:不能自由构造对象、在应用中只存在一个实例的情况。
例如:图片加载器中一般都含有这么几个部分:RequestManager(请求管理器)、线程池、Engine(数据获取引擎)、MemoryCache、DiskLRUCache、Transformation(图片处理)、Target等模块,很消耗资源,没有必要也不应该创建多个实例。
写法有多种,下面分析常用的懒汉单例模式的优缺点,并做出最优选择。
二、常用单例模式分析
1. 传统懒汉模式
代码:
public class Singleton1 {
private static Singleton1 sInstance;
private Singleton1() {
}
public static synchronized Singleton1 getInstance() {
if (sInstance == null) {
sInstance = new Singleton1();
}
return sInstance;
}
}
* 优点:第一次调用getInstance()时才初始化,synchronized保证线程安全
* 缺点:每次调用getInstance()时都会同步,即使sInstance已经初始化过了,造成不必要同步开销
* 结论:不推荐
2. DCL(Double Check Lock)
代码:
public class Singleton2 {
private static Singleton2 sInstance;
private Singleton2() {
}
public static Singleton2 getInstance() {
if (sInstance == null) {
synchronized(Singleton2.class) {
if (sInstance == null) {
sInstance = new Singleton2();
}
}
}
return sInstance;
}
}
getInstance()方法进行了两次判空,第一次是为了避免不必要的同步,第二次则是在null的情况下创建实例。
看起来很好但是存在问题:因为sInstance = new Singleton2()这句不是原子操作(可能会被打断),它可分为3步:
- 给sInstance分配内存
- 调用构造函数初始化成员变量
- 将sInstance指向分配的内存空间(执行完本操作后,sInstance != null)
假设:线程A执行到sInstance = new Singleton2()这步,走的顺序为1->3->2,当执行完3后,线程B直接取走了非空的sInstance(但并没有初始化),使用时就会出错,导致DCL(Double Check Lock)失效
* 优点:懒加载,第一次执行getInstance()时单例对象才会实例化,资源利用率高、效率高;
* 缺点:高并发时有风险(小概率发生错误);第一次加载稍慢
* 结论:能满足绝大部分使用场景,推荐使用
3.DCL改善
代码:
public class Singleton3 {
private volatile static Singleton3 sInstance;
private Singleton3() {
}
public static Singleton3 getInstance() {
if (sInstance == null) {
synchronized (Singleton3.class) {
if (sInstance == null) {
sInstance = new Singleton3();
}
}
}
return sInstance;
}
}
在DCL基础上,将sInstance设为volatile
volatile:1.保证线程本地不会有变量副本,每次都从主内存中读取;2.保证变量的写操作都先行发生在后面对于它的读操作
* 优点:每次都从主内存中读取sInstance,将DCL中sInstance = new Singleton2()操作原子化,避免错误
* 缺点:多少影响一点性能
* 结论:可以选择
4. 静态内部类单例
代码:
public class Singleton4 {
private Singleton4() {
}
private static class Holder {
private static final Singleton4 INSTANCE = new Singleton4();
}
public static Singleton4 getInstance() {
return Holder.INSTANCE;
}
}
* 优点:懒加载、线程安全、保证单例对象的唯一性(静态内部类只会被加载一次)
* 缺点:无
* 结论:最佳选择
5. 枚举单例
代码:
public enum Singleton5 {
INSTANCE
}
* 优点:写法简单
* 缺点:枚举比较占资源,官方不是很推荐
* 结论:不是很推荐
三、结论
最佳选择: 4. 静态内部类单例
第二选择:2. DCL
第三选择:3. DCL改善
(个人看法)