频道栏目
首页 > 资讯 > 其他 > 正文

设计模式—单例设计模式总结

17-04-21        来源:[db:作者]  
收藏   我要投稿

单例设计模式总结

单例模式简介

单例模式是应用最广泛的模式之一,在应用这个模式时,单例对象的类必须确保只有一个实例存在,避免产生多个对象消耗过多的资源。如在访问IO 和数据库时可以考虑使用单例模式。

单例模式的 UML 类图

来源于网络

Client - 高层客户端;

Singleton - 单例类;

实现单例模式的关键点

1). 构造函数私有化;

2). 通过一个静态方法或枚举返回单例类对象;

3). 确保单列类对象有且只有一个,尤其是在多线程环境下;

4). 确保单列类对象在反序列化是不会重新构建对象;

几种单列模式

1- 懒汉模式

public class Singleton

{

private static Singleton sInstance;

private Singleton()

{

}

public static synchronized Singleton getInstance()

{

if (sInstance == null)

{

sInstance = new Singleton();

}

return sInstance;

}

}

优点:只有在使用时才会被实例化,在一定程度上节约了资源;

缺点:第一次加载时需要及时实例化,每次调用 getInstance() 方法都进行同步,造成不必要的同步开销。

这种模式一般不建议使用。

2- 饿汉模式

public class Singleton

{

private static Singleton sInstance = new Singleton();

private Singleton() {}

public static synchronized Singleton getInstance()

{

return sInstance;

}

}

优点:类加载时实例化,避免了线程不同步问题;

缺点:无法对singleton实例做延迟加载。

3 - Double Check Lock(DCL) 模式

public class Singleton

{

// private static Singleton sInstance = null;

//JDK 1.5 以后写法

private static volatile Singleton sInstance = null;

private Singleton() {}

public static Singleton getInstance()

{

if (sInstance == null)

{

synchronized (Singleton.class)

{

if (sInstance == null)

{

sInstance = new Singleton();

}

}

}

return sInstance;

}

}

为什么 JDK1.5以后版本改变了写法呢?

这是因为 sInstance = new Singleton() 语句并非是一个原子操作,这句代码最终会被编译成多条汇编指令,大致做了3件事情:

(1)给 Singleton 的实例分配内存;

(2)调用 Singleton() 的构造函数,初始化成员字段;

(3)将 sInstance 对象指向分配的内存地址(此时 sInstance 就不是 null了);

由于 Java 编译器允许处理器乱起执行,以及 JDK1.5之前 JMM(Java Memory Model,即 Java 内存模型)中 Cache、寄存器到主内存会写顺序的规定,(2)和(3)的顺序是无法保证的,执行顺序可能是 1- 2 - 3也可能是 1 - 3 - 2,如果是后者,假设线程A在(3)执行完毕,(2)未执行时,此时被切换到线程B上,这时 sInstance 因为线程A已经执行过了(3),sInstance 已经是非空了,线程B直接获取 sInstance,再使用时就会出错,这种错误难以跟踪及发现,所以在 JDK1.5版本之后,调整了 JVM 具体化了 volatile 关键字,因此将 sInstance 的定义改成了

private static volatile Singleton mInstance = null;

就可以保证 sInstance 对象每次都是从主内存中读取。

优点:资源利用率高,第一次执行 getInstance() 时单例对象才会被实例化,效率高;

缺点:第一次加载时反应稍慢,也由于 Java 内存模型的原因偶尔会失败。

DCL 模式是使用最多的单例实现方式。

4 - 静态内部类模式

public class Singleton

{

private Singleton() {}

public static Singleton getInstance()

{

return SingletonHolder.sInstance;

}

private static class SingletonHolder

{

private static final Singleton sInstance = new Singleton();

}

}

当第一次加载 Singleton 类时并不会初始化 sInstance ,只有在第一次调用 getInstance() 方法时才会初始化 sInstance 。

优点: 确保线程安全并保证单例对象的唯一性。

推荐使用此单例模式实现方式。

5 - 枚举单例模式

public enum Singleton

{

INSTANCE;

public void log()

{

//....

}

}

优点: 写法简单,线程安全,并且在任何情况下它都是一个单列。

为什么说枚举单例是在任何情况下都是一个单列呢?那就是反序列化。

1至4的单例模式中,在反序列化情况下会出现重新创建对象的情况,反序列化时依然可以通过特殊的途径去创建一个类的实例,相当于调用该类的构造函数。上述实例中如要杜绝单例对象被反序列化时重新生成对象,必须加入如下的方法:

private Object readResolve() throws ObjectStreamException

{

return sInstance;

}

在 readResolve() 方法中将 sInstance 对象返回,而不是默认的重新生成一个新的对象,对应枚举却不存在这个问题,因此即使反序列化也不会重新生成一个新的实例。

总结 无论哪种方式实现的单例模式,核心原理都是将构造函数私有化,并且通过静态方法获取一个唯一的实例,获取过程中必须保证线程安全、防止反序列化导致重新生成实例对象的问题。

以上总结内容参考 Android源码设计模型解析与实战 一书,书中详细阐述了各种设计模式及在 Android 源码中的使用,对设计模式想深入了解的同学,此书值得一看。

相关TAG标签
上一篇:通过注解约束数据类型(主要是约束int)
下一篇:使用cocopods导入第三方库报错解决办法
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站