设计模式系列1---独当一面的单例

在开始写之前我想吐槽下CSDN! 在上面写的源码探索系列6和14居然不见了,上次找版主去找回一次别的文章! 这次居然跑了两篇,服务器要高可用啊!

现在还记得14写的内容是HandleThread,但第六篇那么久那里还记得写的是哪篇,想补都补不回来了!
(更新:12/30, 记起来了,那个第六篇是四大金刚之一的广播,唉,那篇写了好久才整理出来的,跪了!)

为了写这篇文章,内心压制了好久,那些文章都是我每天下完班辛辛苦苦熬到凌晨一两点才写完的! 翻看了那么多代码,熬了这么久!真的内心难以忍受这样的错误!

一个可以让人舒服写字的地方真不容易找!

起航

我们的单例模式在我们开发安卓的过程多多少少会遇到,或者我们在写的过程就可能写过了。 他的最原始的样子像下面这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MySingleWork {

private static MySingleWork mySingleWork;

MySingleWork() {
}

public static MySingleWork getInstance() {
if (mySingleWork == null) {
mySingleWork = new MySingleWork();
}
return mySingleWork;
}
}

看上面的例子,我们可以看到一个基本的模型:

  1. 一个静态的实例变量
  2. 一个私有的构造函数
  3. 一个获取这个静态实例的静态函数

这个就是单例的基本套路,他起源于对资源的控制上要求有且只有一人,真像我们以前的皇帝一样,一个国家只能有一个皇帝,如果有两个,变成双重心,那可是要出问题的啊。就像你有两个脑袋,一个说要前进,一个说要后退,你说该怎么办呢?所以最好只有一个负责人。因此我们需要一个独当一面的东西出来。 这个就是单例(Singleton

但上面的单例是有点小问题的,不过如果不涉及多线程,多进程的话,那就没什么问题。 所有的问题基本是确保是否唯一效率的问题! 上面的单例模式的问题就是可能会导致不惟一,当有多个线程并发的调用MySingleWork.getInstance()的时候,就有可能闹问题啦! 维持我们加了一个同步锁synchronized,这个加锁也挺有学问的,加的方式也挺多的,下面列几个曾经看到的。

版本:

1
2
3
4
5
6
 public synchronized static MySingleWork getInstance() {
if (mySingleWork == null) {
mySingleWork = new MySingleWork();
}
return mySingleWork;
}

版本2:

1
2
3
4
5
6
7
8
public synchronized static MySingleWork getInstance() {
synchronized (MySingleWork.class) {
if (mySingleWork == null) {
mySingleWork = new MySingleWork();
}
return mySingleWork;
}
}

版本3:

1
2
3
4
5
6
7
8
9
10
public synchronized static MySingleWork getInstance() {
if (mySingleWork == null) {
synchronized (MySingleWork.class) {
if (mySingleWork == null) {
mySingleWork = new MySingleWork();
}
}
}
return mySingleWork;
}

这三个版本,显然是第三个最好,因为其实我们大部分的情况是实例化了,再继续调用的情况。即多次的调用,这时候如果还有synchronized 要面对,效率就降下来了。虽然第三版的第一次效率低,不过后面就没有影响了,是笔合理的代价,而且在很多高并发情况,这个也是不会有错误的,除了多进程的情况。

不过这个模式还是存在失效的情况,到底怎么个失效法呢,欢迎看下这里,清英大神的并发编程网里面的一篇完美的单例实现(The Perfect Singleton)对这个问题的讨论,这里复制下解决方案,静态内部类单例模式

1
2
3
4
5
6
7
8
9
10
11
12
public class MySingleWork {

private MySingleWork() { }

public synchronized static MySingleWork getInstance() {
return SingletonHolder.mySingleWork;
}

private static class SingletonHolder {
private static MySingleWork mySingleWork = new MySingleWork();
}
}

可以看到,这里由一个静态的内部类来持有,可以满足线程安全,而且知道我们调用getInstance的时候。才会去生成,即延迟加载。

除了以上,还有遇到过一种容器单例模式,这个在我们的源码探索系列里面有提到过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ContainerSingleton {

private static Map<String, Object> singletonList = new HashMap<>();

private ContainerSingleton() {
}

public static Object getInstance(String key) {
return singletonList.get(key);
}

public static void registerInstance(String key, Object singletonObject) {
if (!singletonList.containsKey(key)) {
singletonList.put(key, singletonObject);
}
}
}

通过这个方式,我们把单例根据key-value对来保存,然后后面需要的就在获取。 这种看起来不是很想前几个常见的模式。

但有一点不变,某东西只有一个,不会重复的原则!

后记

本来还想继续补充点内容,但今天的心情真的一般般,特别是看到我的文章少了,下次有空再继续补充。

由于传图不怎么方便,关于UML的设计等图片就略了,下次看有什么优雅的方法提高下效率吧。

自己学可以有多一些的思考,就像看别人写的代码一样,觉浅需行。 书上看的时候觉得浅,想彻底掌握还是需要自己亲行力践

热评文章