大话设计模式之单例模式
目录
1.引入单例模式
在开始之前,咱先来考虑一下问题,对于一些对象,我们只需要一个,比如线程池,缓存等,这类对象只能有一个实例,如果制造出多个实例,会出现问题的,如行为异常,资源使用过量等。那这种情况怎么弄呢?
有人说这直接用全局变量不就行了,可是如果用全局的静态变量,那有可能最终程序根本没用到,导致资源的浪费。
那么就要说道下面要说的,单例模式,这个应该是设计模式中最简单的啦。
2.什么是叫单例模式?
单例模式就是保证在整个应用中某个实例有且只有一个,不能通过任何方法实现多个实例的创建。
3. 单例模式包括多少种?
a.饿汉模式(他没吃饱,要快速的吃饱)
具体的过程就是先将构造方法私有化,使得外部不能通过new方法创建,然后在类加载的时候,就创建唯一的对象instance,最后提供一个供外部获取实例instance的方法getInstance。
public class SingletonA {
//1.构造方法私有化,不允许外部直接创建
private SingletonA(){
}
//2.创建类的唯一实例
private static SingletonA instance=new SingletonA();
//3.提供一个用于获取实例的方法,采用public static修饰
public static SingletonA getInstance(){
return instance;
}
}
b.懒汉模式(他吃饱了,等饿的时候再吃)
具体的过程就是先将构造方法私有化,使得外部不能通过new方法创建,然后在类加载的时候,声明instance对象,但并不创建,最后提供一个供外部获取实例instance的方法getInstance,如果instance为null,也就是未创建instance对象时,就创建一个对象instance,如果已经创建了instance对象,就直接返回对象。
public class SingletonB {
//1.构造方法私有化,不允许外部直接创建
private SingletonB(){
}
//2.创建类的唯一实例
private static SingletonB instance;
//3.提供一个用于获取实例的方法,采用public static修饰
public static SingletonB getInstance(){
if(instance==null){
return new SingletonB();
}
return instance;
}
}
c.两者的区别
- 速度方面:
饿汉模式:该模式在类加载的时候,就创建了对象。这样就导致了他加载类的速度慢,因为要创建对象,但是运行时获取对象时速度快,因为类加载的时候已经创建好了,直接使用就行。
懒汉模式:该模式在类加载的时候,只是声明了对象,并没有创建。这样就导致了他加载类的速度快,因为不需要创建对象,但是运行时获取对象时速度慢,因为类加载的时候并没有创建,在运行的时候要创建。
- 线程安全性方面
饿汉模式:因为在类加载的时候就创建了对象,没有同时加载的情况,所以线程是安全的。
懒汉模式:因为在类运行的时候才创建了对象,存在两个线程同时调用getInstance方法,所以有创建两个对象的可能性,即线程是不安全的。
4.传统单例模式有什么缺点?
前面说了懒汉是线程不安全的。多个线程同时进行时,会造成创建多个实例。
5.怎么对传统单例模式进行改进?
a.使用synchronized进行单重锁定
public class Singleton {
//1.构造方法私有化,不允许外部直接创建
private Singleton(){
}
//2.创建类的唯一实例
private static Singleton instance;
//3.程序运行时创建一个静态只读的进程辅助对象
private static final Object syncRoot=new Object();
//3.提供一个用于获取实例的方法,采用public static修饰
public static Singleton getInstance(){
//在此处只有一个线程可以进入
synchronized (syncRoot){
if(instance==null){
return new Singleton();
}
}
return instance;
}
}
缺点:十分影响性能,每次进来都加锁,速度太慢啦。
b.使用synchronized进行双重锁定
public class Singleton {
//1.构造方法私有化,不允许外部直接创建
private Singleton(){
}
//2.创建类的唯一实例
private static Singleton instance;
//3.程序运行时创建一个静态只读的进程辅助对象
private static final Object syncRoot=new Object();
//3.提供一个用于获取实例的方法,采用public static修饰
public static Singleton getInstance(){
//判断实例是否存在,不存在再进行加锁
if(instance==null){
//在此处只有一个线程可以进入
synchronized (syncRoot){
if(instance==null){
return new Singleton();
}
}
}
return instance;
}
}
存在两个instance判空的原因:
比如有两个进程A,B,他们同时调用getInstance方法,且instance=null,他们都将通过第一层判断,由于synchronized,只有A进入,并创建了instance对象,如果没有第二层判断,当A退出的时候,又要新建一个instance。