-
单例模式定义
Ensure a class has only one instance,and provide a global point of access to it.(确保一个类只有一个实例,并且自行实例化并向整个系统提供这个实例 )
-
举个栗子
-
饿汉式
不知道大家上学的时候看没看过辰东的一部小说“遮天”,这部小说里对于最高等级的人物称之为大帝。并且在一段时期内古之大帝只能有一位,万古唯一。现在就拿这个场景举个栗子。
首先,我们先创建一个大帝类,如下
/**
* 古之大帝类
*/
public class Emperor {
//内部对类实例化
private static final Emperor emperor = new Emperor();
//构造函数私有化,确保外部不能随意实例化创建对象
private Emperor(){}
//对外提供一个方法来返回实例化的对象
public static Emperor newInstance(){
return emperor;
}
public static String say(){
return "吾为古之大帝,万古唯一.........";
}
}
其次,创建一个场景类,如下
public class ClientMain {
public static void main(String[] args) {
//这里的i代表时间,单位万年
for(int i = 0; i < 3; i++){
Emperor emperor = Emperor.newInstance();
String s = Emperor.say();
System.out.println((i+1)+"万年过去了"+emperor+":"+ s);
}
}
}
我们运行一下这个场景类,可以看到
注意看我用红框标注出来的地方,可以看出这个地址是没有变化的,是唯一的。以上就是单例模式最简单的一个实现方式,也被称作饿汉式。总结一下就是,第一步:构造方法私有化,内部实例化对象;第二步:创建一个静态方法用来给外界提供该对象。
-
懒汉式
上面那种类一加载就会在内部实例化对象的方式是饿汉式。饿汉式有一个缺点就是有可能会造成资源的浪费,就是如果你永远不会调用newInstance()方法的话,就用不到这个实例,但是这个实例已经在内存中了。同样,事物都有两面性,饿汉式在多线程的使用场景中,不会存在线程不安全的问题。
下面,我们介绍的懒汉式正好与饿汉式相反,我们把上面的例子稍作修改,如下
public class Emperor {
private static Emperor emperor = null;
//构造函数私有化,确保外部不能随意实例化创建对象
private Emperor(){}
//对外提供一个方法来返回实例化的对象
public static Emperor newInstance(){
if (emperor == null){
emperor = new Emperor();
}
return emperor;
}
public static String say(){
return "吾为古之大帝,万古唯一.........";
}
}
运行场景类,可以看到
这个古之大帝还是万古唯一的。但是,上面也提到了这种懒汉式在多线程的使用场景中是存在问题的,为甚麽这莫说呢,看下图
当线程1执行完new Emperor();时,但是还没有return。这时,又来了个线程2进行emperor是否为null的if判断,这时的emperor是为null的。这样就会出现两个Emperor实例的情况,不符合单例设计模式的原则了。
那怎末解决呢?我们可以通过加锁的方式解决这个问题,如下
/**
* 古之大帝类
*/
public class Emperor {
private static Emperor emperor = null;
//构造函数私有化,确保外部不能随意实例化创建对象
private Emperor(){}
//对外提供一个方法来返回实例化的对象
public static synchronized Emperor newInstance(){
if (emperor == null){
emperor = new Emperor();
}
return emperor;
}
public static String say(){
return "吾为古之大帝,万古唯一.........";
}
}
通过synchronized关键字可以避免多线程场景下的使用问题,但是这样的话效率相对于饿汉式来说的话不高。以上就是我对单例模式中懒汉式与饿汉式的理解。
当然了,单例模式不仅仅只有懒汉式与饿汉式这两种实现方式。比如,Mybatis中的Configuration在整个Mybatis中也是单例的,但是它所实现的方式既不是懒汉式也不是饿汉式,有兴趣的话可以自己去看看源码。
-
使用场景
在一个项目中需要一个共享访问点或者共享数据,比如Mybatis中的Configuration。