单例模式及常⽤使⽤场景简析
设计模式-单例模式简介及使⽤场景
单例模式
顾名思义,单例模式(Singleton Pattern)就是保证⼀个类有且仅有⼀个实例,并且提供了⼀个全局的访问点。这就要求我们绕过常规的构造器,提供⼀种机制来保证⼀个类只有⼀个实例,客户程序在调⽤某⼀个类时,它是不会考虑这个类是否只能有⼀个实例等问题的,所以,这应该是类设计者的责任,⽽不是类使⽤者的责任。
单例类的特点总结如下:
1. 只能有⼀个实例
2. 构造⽅法应该由private修饰,也就是⾃⼰创建⾃⼰的唯⼀实例
3. 必须给其他对象提供这⼀实例
实现单例模式的⼏种⽅式
上⽂已经提到,单例类要实现有且仅有⼀个实例必须要设计者对该类进⾏处理,下⾯就简单介绍⼀下如何设计⼀个单例类。
1. 饿汉模式
public class MySingleton {
//⾃创的单例
private static final MySingleton instance =new MySingleton();
//私有的单例构造⽅法,避免外部创建
private MySingleton(){
}
//公有的静态⽅法,为外部提供唯⼀的单例类
public static MySingleton getInstance(){
return instance;
}
}
从上⾯代码可以看出,饿汉模式在类加载初始化时就创建好⼀个静态的对象供外部使⽤,除⾮系统重启,这个对象不会改变,所以本⾝就是线程安全的。通过⽤private修饰构造⽅法,保证了外部不能对该单例类进⾏实例化。
2. 懒汉模式
public class MySingleton {
private static MySingleton instance = null;
// 私有构造
private MySingleton(){}
// 静态⼯⼚⽅法
public static MySingleton getInstance(){
if(instance == null){
instance =new MySingleton();
}
return instance;
}
}
上⾯就是懒汉式单例的简单写法,可以看到在多线程情况下会产⽣线程安全问题。假设MySingleton类刚刚被初始化,instance为空,此时两个线程同时访问getInstance⽅法,因为instance为空,所以两个线程同时通过了条件判断,都new出了⼀个MySingleton实
例,MySingleton类被构建了两次。为了解决这个问题,可以使⽤双重检测机制进⾏约束,代码如下:
上⾯代码看似没任何问题,但当两个线程⼀先⼀后访问getInstance⽅法的时候,当A线程正在构建对象,B线程刚刚进⼊⽅法,如下图所⽰。此时线程A要么已经构建完instance(线程B判断时返回false),
要么还未构建完instance(线程B判断时返回true),但事实并⾮如此,这⾥涉及到JVM编译器的指令重排,也就是java中简单的⼀句 instance = new Singleton,会被编译器编译成如下JVM指令:
线程安全的单例类写法如下:public class MySingleton {
public class MySingleton {
//内部类
private static class InnerObject {
private static final Singleton instance =new MySingleton();
}
//私有的单例构造⽅法,避免外部创建
private MySingleton(){
}
//公有的静态⽅法,为外部提供唯⼀的单例类
public static MySingleton getInstance(){
return InnerObject.instance;
}
}
上⾯⽅法需要注意的是instance对象并不是在单例类Singleton被加载的时候初始化的,⽽是在调⽤getInstance⽅法是通过静态内部类加载。因此这种实现⽅式是利⽤classloader的加载机制来实现懒加载,并保证构建单例的线程安全。
4. static静态代码块实现
public class MySingleton {
//⾃创的单例
private static final MySingleton instance = null;
//私有的单例构造⽅法,避免外部创建
private MySingleton(){
}
static{
instance =new MySingleton();
}
//公有的静态⽅法,为外部提供唯⼀的单例类
public static MySingleton getInstance(){
return instance;
}
}
5. 枚举类实现
上述所说的单例类实现⽅式均可通过反射来访问单例类中的私有构造⽅法,从⽽构造出多个单例对象,这和单例对象的定义违背,我们可以通过设计内部枚举类来防⽌该类问题。枚举类单例不是懒加载,其单例对象是在枚举类被加载时初始化的。
public enum MySingletonEnum {
单例模式的几种实现方式instance;
}
单例模式的常见应⽤场景
单例模式应⽤的场景⼀般发现在以下条件下:
1. 资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的⽇志⽂件,应⽤配置。
2. 控制资源的情况下,⽅便资源之间的互相通信。如线程池等。
根据上诉条件列出以下常⽤的应⽤场景:
1. 外部资源:每台计算机有若⼲个打印机,但只能有⼀个PrinterSpooler,以避免两个打印作业同时输
出到打印机。内部资源:⼤多数
软件都有⼀个(或多个)属性⽂件存放系统配置,这样的系统应该有⼀个对象管理这些属性⽂件
2. Windows的(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager
吗? 不信你⾃⼰试试看哦~
3. windows的(回收站)也是典型的单例应⽤。在整个系统运⾏过程中,回收站⼀直维护着仅有的⼀个实例。
4. ⽹站的计数器,⼀般也是采⽤单例模式实现,否则难以同步。
5. 应⽤程序的⽇志应⽤,⼀般都何⽤单例模式实现,这⼀般是由于共享的⽇志⽂件⼀直处于打开状态,因为只能有⼀个实例去操作,否
则内容不好追加。
6. Web应⽤的配置对象的读取,⼀般也应⽤单例模式,这个是由于配置⽂件是共享的资源。
7. 数据库连接池的设计⼀般也是采⽤单例模式,因为数据库连接是⼀种数据库资源。数据库软件系统中使⽤数据库连接池,主要是节省
打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是⾮常昂贵的,因为何⽤单例模式来维护,就可以⼤⼤降低这种损耗。
8. 多线程的线程池的设计⼀般也是采⽤单例模式,这是由于线程池要⽅便对池中的线程进⾏控制。
9. 操作系统的⽂件系统,也是⼤的单例模式实现的具体例⼦,⼀个操作系统只能有⼀个⽂件系统。
好了,以上就是单例类的⼏种常见实现⽅式和⽤⽤场景,参考⽂章
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论