Java设计模式——单例模式

单例模式由于只创建了唯一对象可以避免资源的多重占用,减少内存的开销,对于经常性使用对象的类来说,单例是一个不错的选择,使用场景,比如:文件操作、共享资源等等。

1、饿汉单例模式

这是最为简单的也是最基本的单例模式,相信大家都写过!先上代码:

public class Single{
   private static final Single instance = new Single();
    private Single(){}
    public static Single getInstance(){
        return instance;
    }
}

由于把构造函数变为了private,所以要想获得该类的实例需要通过getInstance(),而不是手动去new一个,这仅仅是最为简单粗暴的单例模式。

2、懒汉单例模式

public class Single{
    
    private static Single instance = null;
    private Single(){}
    public static synchronized Single getInstance(){
        if (instance == null){
            instance = new Single();
        }
        return instance;
    }
   
}

懒汉单例模式看起来和饿汉单例模式差不多,就多了一个 synchronized 关键字,也就是说该模式是同步的方法单例。在 getInstanc()方法中,可以清楚知道不管该类对象是否已经实例了(实际上第一次调用的时候只new了一次),都会进行同步,这样确实每次都同步确实耗费了不必要的资源和内存,而且在加载的时候都会事先 synchronized,所以会比较耗时间,所以不太建议使用。

3、Double Check Lock(DCL)

public class Single{
    
    private static Single instance = null;
    private Single(){}
    public static Single getInstance(){
        if (instance == null){
            synchronized (Single.class){
                if (instance == null){
                    instance = new Single();
                }
            }
        }
        return instance;
    }
}

在代码中可以看到,在调用 getInstance() 方法的时候会检查类的实例是否为空,true则直接返回该实例,false则会 synchronized (利用Single.class来对本类对象同步),如果为null则会new一个对象,否则直接返回。这意思很清楚,这里有两次判断是否为null的步骤,第一步判断是为了避免不必要的同步,而第二步则是同步检查。
  我们知道在new,即instance = new Single()的时候,一般会有三个步骤:1、分配内存;2、调用构造函数并初始化成员字段;3、为对象指向分配好的空间。而Java是允许处理器乱序执行的,还有JDK版本原因,很多时候这三个步骤很可能不是按照顺序执行的,
所以在多线程的操作下,如果在A线程中执行某一步的时候,B线程也调用了该方法,而这时候A线程刚刚好分配内存成功(假设第一个调用)但未调用构造函数创建对象,所以这时候B在检查的时候对象已经非空了(因为已经指向了内存),所以B线程会直接使用对象instance,很显然,由于还没调用构造函数,所以B线程使用的时候会出问题。这叫DCL失效。虽然说这是很小的概率问题,但还是会长期隐藏着问题的。不过从JDK1.5之后,sun注意了这个问题,把这个bug修改了过来,只要 如此声明:private volatile static Single instance = null 就可以保证instance 是从主内存取出来的,虽然volatile 会影响性能,但为了DCL有效就值得。
  总的来说DCL是饿汉、懒汉单例模式的结合,尽管存在bug,但还是sun已修改了,所以比较建议这种写法。

4 、内部静态类单例模式

  利用静态内部类的特性来返回对象(static关键字不用多讲了吧)

public class Single {

    private Single (){}
    public static Single getInstance(){
        return SingleHolder.instance;
    }
    private static class SingleHolder{
        private static final Single  instance = new Single();
    }

}

  这不仅仅首次调用才初始化,而且线程安全,也能保证对象的唯一性,所以这也是推荐的写法。

5、枚举单例模式

public enum  SingleEnum {
    INSTANCE;
}

在Java中,枚举类型是默认线程安全的,在任何情况下都能保证唯一性,而且写法最为简单。大家可以尝试一下的

6、容器单例模式

public class SingleCollect {
    private static Map<String, Object> objectMap = new HashMap<>();
    private SingleCollect(){}

    /**
     * 根据类名把实例通过Map保存起来
     * @param key  类名
     * @param instance  类的实例对象
     */
    public static void registInatance(String key, Object instance){
        if (!objectMap.containsKey(key)){
            objectMap.put(key, instance);
        }
    }

    /**
     * 根据类名来寻找对应的对象实例
     * @param key
     * @return
     */
    public static Object getInstance(String key){
        return objectMap.get(key);
    }
}

  利用Map把类的对象实例保存起来,在需要的时候根据类名key获取即可。

总结

  通过几种单例模式,核心思想无非是把构造函数私有化,然后利用 public static修饰符来获取对象实例,并且保证线程安全!!!值得注意的时候,我们还需要考虑这么一种情况:反序列化。我们知道可以通过序列化把对象实例写进磁盘,然后再读回来。而反序列化依然可以通过别的途径去重新创建一个新的对象实例,即便是私有的构造函数!上述的几种模式就只有枚举单例模式可以避免。不过,在实际开发中,我们需要结合项目需要,而不是一味地直接使用枚举单例模式,灵活使用单例模式才是正道。
  上文如有不对或者不妥之处,大家记得留言指出哈。一起进步才比较爽啊!!!and then 后续我会继续写一系列关于设计模式的,请大家静候!

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

推荐阅读更多精彩内容

  • 单例模式(SingletonPattern)一般被认为是最简单、最易理解的设计模式,也因为它的简洁易懂,是项目中最...
    成热了阅读 4,222评论 4 34
  • 概念 java中单例模式是一种常见的设计模式,单例模式的写法有好几种,比较常见的有:懒汉式单例、饿汉式单例。单例模...
    怡红快绿阅读 452评论 0 0
  • Java设计模式——单例模式 单例模式应该是大家最为熟知的一种设计模式了,相信大家或多或少的都在自己的项目中使用过...
    gogoingmonkey阅读 506评论 0 2
  • 今天笔试的时候被问到了单例模式,听了很多次,但是却没有认真看过,所以交了白卷,懒的教训啊!还有一题比较有趣的题目:...
    shakesbears阅读 370评论 0 3
  • 阅读原文 在介绍单例模式之前,我们先了解一下,什么是设计模式?设计模式(Design Pattern):是一套被反...
    gyl_coder阅读 149评论 0 3