单例模式

单例模式:保证一个类仅有一个实例,并提供一个访问它全局访问点

比如下面的类就是一个单例模式

普通的单例模式

1
2
3
4
5
6
7
8
9
10
class Singleton {
public static Singleton instance;
private Singletone {}
public getInstance() {
if (instance == null) {
instance = new Singletone();
}
return instance
}
}

以上的类就是不允许你在外部直接 new Singleton,每次获取这个类实例只能通过 Singleton.getInstance() 这种方式来获取,而这个方案保证了只实例化一次类实例

多线程的单例模式
上面的一个模式仅仅对于单线程有用,在多线程的世界里或者是在高并发程序中,这种方法往往是无效的,为什么呢?

因为单线程是从上往下执行的,而多线程是同时一起执行的,所以,里面的 new Singletone() 这个方法在高并发的情况下,肯定会存在多个线程都走到这一步,也就造成了实例化了多次 instance,这显然和单例模式冲突的

我们可以通过加锁来解决这个问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Singleton {
private static Singleton instance;
private static readonly object syncLock = new Object();
private Singletone {}
public getInstance() {
if (instance == null) {
lock(syncLock) { // 疑问1:为什么不直接 lock(instance) ?
if (instance == null) { // 疑问2: 前面已经有判断 instance == null 了,为什么这里还要判断呢?
instance = new Singletone();
}
}
}
return instance
}
}

对比单线程的代码,我们增加多了一把锁来完成

疑问1:为什么不直接 lock(instance) 呢?

因为在加锁的时候还不知道 instance 到底有没有被实例化

疑问2:前面已经有判断instance == null 了,为什么下面还要加上 if (instance == null) 呢?

这是因为当两个线程同时到达第一个判断的时候,必然有一方是先抢占到锁的,那么另一方就只能等待他释放锁(就好像只有一间洗手间,两个人同时进来,那当然只能有一个先进去,而另外一个在里面等待),当第一个线程进去以后,发现没有实例化,那么实例化之后释放锁就 return 了,而第二个线程进来后通过判断发现已经 instance 了,所以就不重复实例化了,也直接 return 了。所以,如果没有 if 判断的话,第二个线程进来以后仍然会继续实例化,这也就和单例模式冲突了。

参考:《大话设计模式》的单例模式

作者

yigger

发布于

2019-07-02

更新于

2024-05-27

许可协议

You need to set install_url to use ShareThis. Please set it in _config.yml.
You forgot to set the business or currency_code for Paypal. Please set it in _config.yml.

评论

You forgot to set the shortname for Disqus. Please set it in _config.yml.