单件/单例模式
定义
单件模式确保一个类只有一个实例,并提供一个全局访问点。即该类对象在内存中只有一个。
例如:计算机系统中的任务管理器、还有 Windows 的回收站、操作系统中的文件系统、多线程中的线程池、显卡的驱动程序对象、打印机的后台处理服务、应用程序的日志对象、数据库的连接池、网站的计数器、Web 应用的配置对象、应用程序中的对话框、系统中的缓存等常常被设计成单件。
单件模式在现实生活中的应用也非常广泛,例如公司 CEO、部门经理等都属于单件模型。
单件模式两种类型:
1.懒汉式:在真正需要使用到对象时才去创建单例类对象
2.饿汉式,在类加载时就去创建单例类对象
UML类图

uniqueInstance:该成员变量持有唯一的Singleton实例
Singleton:私有构造器,保证其他地方不能通过new得到一个Singleton实例对象
getInstance:一个全局访问点。通过这个公共的静态方法可以得到唯一的Singleton实例
使用场景
1.需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少 Gtrea 5rfv5’ ]/ 5v5C
2.某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等
3.某些类创建实例时占用资源较多,或实例化耗时较长,且经常使用
4.某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等
5.频繁访问数据库或文件的对象
6.对于一些控制硬件级别的操作,或者从系统上来讲应当是单一控制逻辑的操作,如果有多个实例,则系统会完全乱套
7.当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等
示例
懒汉式
/**
* 单件模式-懒汉式
*/
public class Singleton1 {
private volatile static Singleton1 singleton1;
// 其他成员变量
// 私有构造器
private Singleton1() {
}
/**
* 双重检查加锁保证多线程下得到的实例唯一
* @return 唯一实例
*/
public static Singleton1 getInstance() {
if (singleton1 == null) {
synchronized (Singleton1.class) {
if (singleton1 == null) {
singleton1 = new Singleton1();
return singleton1;
}
}
}
return singleton1;
}
// 其他成员方法
}
使用volatile关键字防止指令重排:
创建一个对象,在JVM中会经过这三步:
1.为singleton对象分配内存空间
2.初始化singleton对象
3.将singleton对象指向分配好的内存空间
所谓指令重排是指:JVM在保证最终结果正确的情况下,可以不按照程序编码的顺序指向语句,尽可能提高程序性能。因此,在1,2,3步中,可能发生指令重排现象,创建对象时顺序变为1-3-2,会导致多个线程获取对象时,有可能线程A创建对象过程中指向了1-3步骤,此时线程B判断singleton对象已经不为空,获取到还未初始化的singleton对象,就会报NPE异常。具体流程如下图:

饿汉式
/**
* 单件模式-饿汉式
*/
public class Singleton2 {
private static Singleton2 singleton2 = new Singleton2();
// 其他成员变量
// 私有构造器
private Singleton2() {
}
// 获取唯一实例
public static Singleton2 getInstance() {
return singleton2;
}
// 其他成员变量
}
优缺点
优点:
1.单例模式可以保证内存里只有一个实例,减少了内存的开销
2.可以避免对资源的多重占用
3.单例模式设置全局访问点,可以优化和共享资源的访问
缺点:
1.单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则
2.在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象
3.单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则
注意
1.最好不要继承单件类。因为必须把单件的构造器改为public或protected,就不是真正的单件了
2.反射可以获取并改变单件类中的私有构造器,从而创建多个实例,破坏单件
总结
1.单件模式确保程序中,一个类最多只有一个实例
2.单件模式提供访问这个实例的全局全局点
3.在Java中实现单件模式需要私有构造器、一个静态变量、一个静态方法
4.确定在性能和资源上的限制,然后小心的选择适当的方案来实现单件,已解决多线程问题(设计时,我们必须认定所有的程序都是多线程的)
5.如果使用多个类加载器,可能导致单件失效从而产生多个实例


本文详细介绍了单例模式的概念、UML类图、使用场景及优缺点。单例模式确保一个类只有一个实例,常用于内存压力降低、频繁创建对象的场景。文中分别讲解了懒汉式(线程安全的volatile实现)和饿汉式的实现方式,并强调了单例模式的扩展困难和并发测试挑战。最后,提醒了避免单例类继承和反射破坏单例的原则。

2206

被折叠的 条评论
为什么被折叠?



