设计模式之单例模式

从这篇开始我的设计模式学习之旅,以前面对设计模式总觉得是在看天书。果然,生活就是最好的老师,当在社会上摸爬滚打过一番之后,别管多久,都会多少有点收获,这恐怕就是生活的意义吧。闲话不多说,马上入正题:在入正题之前再啰嗦一句,由于本人最熟悉的是java,所以所有的模式几乎都是以java为例的。好!!马上正题:

单例模式

这个模式估计是所有的入坑选手第一个接触到的模式,也是听到最多的一个模式,对于每一个模式几乎都是三步走:是什么(该模式的定义),为什么(使用该模式的原因)以及怎么做(如何去使用,什么时候使用该模式)。所以先来第一步:

单例模式?什么东西?

什么叫模式?你可以通俗的理解为“套路”,那什么是单例?也就是“单一实例”。学java的都知道,一个类(class),是可以创建(new)出来很多对象(也就是实例)的。比如:

public class Person{
    String name;
    public Person(String name){
        this.name = name;
    }
}

这是一个“人”类,通过这个类你可以创建很多人,什么张三,什么李四啊,阿猫阿狗啊等等:

Person zhangsan = new Person("章三");
Person lisi = new Person("李四");

这样对于Person这个类来说就不是单一实例了。<ber>
所以单例模式就是:有没有一种套路可以让我一个类只创建出来一个实例?
可是

我们为什么要单例模式呢?

想象一下,现实中有没有什么是只有一个的,当然哦,不是说全世界只有一个,而是在一个“系统”中只有一个,比如说在一部电脑里面电脑系统只有一个任务管理器,再比如说,在金庸先生的武侠世界里只有一个武林盟主。等等等等。可以看的出来,“单一”的一个好处就是:方便管理。我再举一个代码的例子吧:
现在有一个放钥匙的钥匙包:

public class KeyCase{
   private Map<String,Key> cases = new HashMap();
    //code ....
}

public class Key(
    public int password;
)

现在是这样的,有一个需要用到钥匙的游戏,在每一关,你都可以得到一个钥匙包和一把钥匙。
现在来到了第一到关卡(第一个Activity),得到一条钥匙(Key实例),然后开了门,然后就很自然的将钥匙放到了钥匙包(KeyCase实例)里,等你准备过去第二关(第二个activity)的时候,你发现钥匙包压根带不过去(在android里,普通类是没办法从一个Activity带到另外一个activity的,只有可以序列化的类才可以),那没办法了,只能把钥匙包放下,先过去第二关看看了。等来到第二关(跳转到第二个Activity)的时候才发现,妈呀,上一关的钥匙居然还有用,可你现在会发现,如果你现在再获取到钥匙包这个对象的时候,里面却是空空如也,因为现在的钥匙包已经不是第一关的钥匙包了。如果这里的钥匙包以单例模式创建那就不一样了,因为在整个游戏下来,都只有一个钥匙包。这才更符合设定,毕竟谁闲来无事带几十个钥匙包出门(无奈程度基本跟用六位数密码去保管两位数的钱...无异)。总的来说,单例模式就是在一个系统中某些需要统一的地方,最好的方法就是让他唯一再通俗点,就是当你希望“这个类只有一个就好了”的时候就是使用单例模式的时候。
这样看来在某些方面,某些时候要是能保证单例也是不错的。

单例模式怎么搞?

首先控制实例的个数就是单例最核心的部分了,那么就不能让外界去创建了,因为那是不可控的,所以第一点就是这个类不能被外界new。如何做到不被外界new呢?将构造方法私有化就好了:

public class KeyCase{
   private KeyCase(){}
    //code ....
}

好了,现在外界谁都不能创建钥匙包KeyCase了,问题又来了,那外界该怎么样获得这个钥匙包实例啊?

别人不能,但是自己(钥匙包)可以创建啊。

public class KeyCase{
    private KeyCase keyCase = new KeyCase();
   private KeyCase(){}
   public KeyCase getInstance(){
       return keyCase;
   }
    //code ....
}

但是外界还是拿不到,只有钥匙包的实例才能通过getInstance()方法拿到keyCase这个实例,可是目前也就只有这么一个实例,这样就陷入了一个死循环:要拿到KeyCase的实例首先要有它的实例。很尴尬!!但是别怕,相信大家都见过这两个关键字了,staticfinal,一个static就解决了上面的问题,而final保证了不可变,所以代码最终就变成了这样:

public class KeyCase{
    private static final KeyCase keyCase = new KeyCase();//准备好一个钥匙包,永远不变的钥匙包
   private KeyCase(){}
   public static KeyCase getInstance(){
       return case;//我现在想用了就可以直接拿到,真方便
   }
    //code ....
}

这就是单例模式里面的“饿汉式”,饿汉式将钥匙包的对象keyCase当作食物,饿汉首先准备好食物,等饿的时候(需要的时候)就可以直接拿来吃(用)。

懒汉式

懒汉式是单例的另一种“套路”,而懒汉式的意思就是我很懒,我不想像饿汉那样先准备好,我想等到我饿的时候才去准备。哟呵!够懒的。这样我们就饿汉的代码直接根据意思改:

//饿汉式
public class KeyCase{
    private static KeyCase keyCase ;//我先不准备
   private KeyCase(){}
   public static KeyCase getInstance(){
       if(keyCase == null){//我现在饿了,但是我想要的还没有  
           keyCase = new KeyCase();//那好吧,只能先去准备了  
       }
       return keyCase;//准备好了,开吃! 3
   }
    //code ....
}

好了,饿汉和懒汉两种方式都实现了。但是,有缺陷哦!

目前的饿汉和懒汉的缺陷。

先说饿汉吧。

只能说是个小缺陷,准确点讲,也不算是缺陷,可以算是饿汉式的特点,那就是先准备,如果准备了不用,那就浪费了一点空间,饿汉式就是用空间来换取时间,先创建而消耗了部分空间去换取使用时候的时间,所以饿汉式在类加载的时候会比懒汉式要慢,但是在运行的时候会比懒汉式要快。而且饿汉还是线程安全的。没意思(饿汉式没什么缺陷可说当然没意思),还是来看看懒汉式吧。

线程不安全的懒汉式

上面说到饿汉式是空间换时间,而懒汉式就是时间换空间,这样就有一个问题,那就是线程不安全。如果现在有两条线程执行钥匙包的getInstance方法,就有可能得到两个对象,因为不能保证在同一时间只有一条线程执行到case=new KeyCase();这样代码。

小E:“我知道我知道,加个同步就好了”!(???这位小E同志是哪冒出来的?)

public class KeyCase{
    private static KeyCase keyCase ;
    private KeyCase(){}
    public static KeyCase getInstance(){
       if(keyCase == null){//1
        synchorinzed(KeyCase.class){
           keyCase = new KeyCase();//2          
           }
       }
       return keyCase;
   }
    //code ....
}

我:“不够”!
照着上面的例子,照样还是会有两个线程停留在1处,也就是都已经判断过了,就算排着队去执行2处的代码,还是会有两个实例。
小E:“现在我是真的知道了,再加个判断”:

public class KeyCase{
    private static KeyCase keyCase ;
    private KeyCase(){}
    public static KeyCase getInstance(){
       if(keyCase == null){//1
          synchorinzed(KeyCase.class){
            if(keyCase==null){//3
                keyCase = new KeyCase();//2
            }
           }
       }
       return keyCase;
   }
    //code ....
}

我:“很接近了,但还是不够!”。是的,我也很长一段时间认为上面的是最安全的懒汉式了,然而看了一些大佬的文章我才知道,这样还是不够,缺陷是第一条线程已经执行完了2处的代码,生成了钥匙包的实例,当第二条线程来到3处是依旧有可能得到的信息是keyCase==null,也就是第二条线程不能及时的知道,keyCase已经被第一条线程创建好了。说到这里就说到java的内存模型了。但是这个在这就不细说了,大家知道有这么一种可能就是了。有兴趣的可以看一些有关volatile关键字的文章。这就是解决懒汉式最后一道线程缺陷的关键字,volatile保证了可见性,什么是可见性呢,也就是volatile关键字修饰的变量对于线程们(多个线程)来说,无论是谁修改了这个变量,其他线程对这个行为是可见的,就立马知道。原来A线程这个小子偷偷修改了这个变量。还以为我不知道?我可是偷偷看着呢,不行,先记在本子上!!

线程最安全的懒汉式

所以最后线程安全的懒汉式应该是这样的:

public class KeyCase{
    private static volatile KeyCase keyCase ;
    private KeyCase(){}
    public static KeyCase getInstance(){
       if(keyCase == null){//1
            synchorinzed(KeyCase.class){
                if(keyCase==null){//3
                    keyCase = new KeyCase();//2
                }
            }
        }
        return keyCase;
    }
    //code ....
}

好了,单例模式最经典的两种已经说完了,估计对单例模式还不了解的应该印象深一点了吧,有加深一点点,对于我来说就已经足够了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,717评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,501评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,311评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,417评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,500评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,538评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,557评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,310评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,759评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,065评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,233评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,909评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,548评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,172评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,420评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,103评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,098评论 2 352

推荐阅读更多精彩内容