二学单例模式
哎呀!单例都学烂了,有什么好学的啊?
因为今天看《Head First 设计模式》看到了一些细节部分,决定重新认识一下单例
应用场景
- 线程池
- 缓存
- 注册表
- Windows的回收站
- 资源管理器
- 工厂
- 对话框
- 偏好设置
- …
普通的单例
我们先来看看一个懒汉式的写法(要用再new)
public Single{
private static Single singleInstance;
private Single(){}
public static Single getSingleInstance(){
if(singleInstance == null){
singleInstance = new Single();
}
return singleInstance;
}
}
很简单的一个例子,大家都会写,把构造隐藏起来,把方法暴露出去,blablabla…
但是这涉及到多线程的话,如果我两个线程同时到达if(singleInstance == null)
,怎么办,这就会出来两个不同的对象啊,这是不安全的,那怎么办呢?
线程安全性
处理多线程问题,我们有三种方法
方法一:提前new好
这叫饿汉式,别嫌代码臭嗷~
public Single{
private static Single singleInstance = new Single();
private Single(){}
public static Single getSingleInstance(){
return singleInstance;
}
}
JVM加载这个类时,马上就会创建好这个对象。如果在创建和运行方面压力不大,你又急着用,确实可以这么做
方法二:增加同步(synchronized)块
还记得synchronized
吗?
public Single{
private static Single singleInstance;
private Single(){}
public static synchronized Single getSingleInstance(){
if(singleInstance == null){
singleInstance = new Single();
}
return singleInstance;
}
}
在方法中间加上synchronized
关键字,问题解决了,但如果我对象都已经创建好了了,我直接拿不久行了,也搁哪儿等着?所以,随之而来的是性能的灾难…(可能差几十倍甚至一百倍)
方法三:减少同步,使用双重锁DCL(Double-Checked Lock)
先检查单例对象创建了没,没创建再同步,而不是不管三七二十一,全同步了
public Single{
private volatile static Single singleInstance;
private Single(){}
public static Single getSingleInstance(){
if(singleInstance == null){
synchronized(Single.class){
if(singleInstance == null){
singleInstance = new Single();
}
}
}
return singleInstance;
}
}
利用双重锁机制,性能🆙
单例 vs 全局变量
还记得我“二学static”里面的例子吗?
//Code.1
public Single{
static public void FuncA(Object o);
static public void FuncB(Object o);
static public void FuncC(Object o);
}
// Code.2
public Single{
private static Single singleInstance;
private Single(){}
public static Single getSingleInstance(){
if(singleInstance == null){
singleInstance = new Single();
}
return singleInstance;
}
public void FuncA(Object o);
public void FuncB(Object o);
public void FuncC(Object o);
}
// 分别调用FuncA
Object o = new Object();
// call 1
Single.FuncA(o);
// call 2
Single.getSingleInstance().FuncA(o);
除了我之前文章说的不同,还存在初始化的和控制权不同的问题
初始化
- 单例延迟初始化
- 类加载后的初始化时间在Java程序
控制权
- 单例控制权在我们
- 全局变量控制权在Java