分享

面试被问设计模式?不要怕看这里:单例模式

 lguo001 2017-01-11


来源: 伯乐在线 - 熊绎

http://blog./109449/

如有好文章投稿,请点击 → 这里了解详情

如需转载,发送「转载」二字查看说明


设计模式是老生常谈的问题,有人工作多年却对设计模式一窍不通,但是更多的人是懂一点点,但是不求甚解。其实这样不好,暂且不说在工作中的应用,即便是在面试时,被面试官问到设计模式时一脸懵逼,是非常尴尬的事情。本文不废话,不谈大篇理论教学,只针对面试,给出设计模式的关键点,从应试的角度,让大家认识和理解设计模式。


首先搞清楚一点,设计模式不是高深技术,不是奇淫技巧。设计模式只是一种设计思想,针对不同的业务场景,用不同的方式去设计代码结构,其最最本质的目的是为了解耦,延伸一点的话,还有为了可扩展性和健壮性,但是这都是建立在解耦的基础之上。


我们都知道大名鼎鼎的GoF的21种设计模式,看过head first的这本书的人应该不少。针对这21种设计模式,面试官问出的问题可能千变万化,让人莫名的忧(dan)伤(teng),但是不要怕,只要你搞清楚了每种设计模式的关键点和精髓,就可以举一反三,迎刃而解了。


今天我们来看看最简单的单例模式。理论我就不介绍了,没听说过的可以去查一下。即便是最简单的单例,也有关键点。举个不太恰当地例子,看过修仙修道之类小说的同学都知道,一般阵法高手做阵都有一个或多个“阵眼”,同理,我们每种设计模式也有“阵眼”,那么单例的“阵眼”在哪里?各位莫慌,我们先来看看代码。根据单例模式的理论:保证系统中只有一个实例,于是我撸了以下代码


public class Singleton {  

    private Singleton() {}                     //关键点0:构造函数是私有的

    private static Singleton single = null;    //关键点1:声明单例对象是静态的

    public static Singleton GetInstance()      //通过静态方法来构造对象

    {                        

         if (single == null)

         {                                     //关键点2:判断单例对象是否已经被构造

             single = new Singleton();  

         }    

        return single;  

    }  

}


好了,如果我是面试官,你是候选人,我要你撸个单例给我,你撸以上代码问我资词不资词,我肯定是不资词的。为什么?真的不是因为我想搞个大新闻,而是这段单例的代码一般情况下还是可以勉强运行,但是,遇到多线程的并发条件下就大清药丸了。因为这段代码是线程不安全的,有可能给new出多个单例实例,都多个了,还是屁的“单例”啊。


好,废话不多说,继续撸代码,你可能会说:”不是说线程不安全吗?小爷我加上线程安全判断呗,度娘在手天下我有,来了您呐~~~“


public class Singleton {  

    private Singleton() {}                     //关键点0:构造函数是私有的

    private static Singleton single = null;    //关键点1:声明单例对象是静态的

    private static object obj= new object();

    public static Singleton GetInstance()      //通过静态方法来构造对象

    {                        

         if (single == null)                   //关键点2:判断单例对象是否已经被构造

         {                            

            lock(obj)                          //关键点3:加线程锁

            {

               single = new Singleton();  

             }

         }    

        return single;  

    }  

}


好了,这回该消停了吧,锁也加了,线程也安全了。But,你还是太连清。。。这样依然有问题。问题在哪里?就在关键点2,检测单例是否被构造。虽然这里判断了一次,但是由于某些情况下,可能有延迟加载或者缓存的原因,只有关键点2这一次判断,仍然不能保证系统是否只创建了一个单例,也可能出现多个实例的情况,那么怎么办呢?


public class Singleton {  

    private Singleton() {}                     //关键点0:构造函数是私有的

    private static Singleton single = null;    //关键点1:声明单例对象是静态的

    private static object obj= new object();

    public static Singleton GetInstance()      //通过静态方法来构造对象

    {                        

         if (single == null)                   //关键点2:判断单例对象是否已经被构造

         {                            

            lock(obj)                          //关键点3:加线程锁

            {

               if(single == null)              //关键点4:二次判断单例是否已经被构造

               {

                  single = new Singleton();  

                }

             }

         }    

        return single;  

    }  

}


所以,在判断单例实例是否被构造时,需要检测两次,在线程锁之前判断一次,在线程锁之后判断一次,再去构造实例,这样就万无一失了。是不是很简(Tu)单(Xue)呢?


最后,我们来归纳一下。下次面试别人再问你单例模式,你可以这样说:


单例是为了保证系统中只有一个实例,其关键点有5


一.私有构造函数


二.声明静态单例对象


三.构造单例对象之前要加锁(lock一个静态的object对象)


四.需要两次检测单例实例是否已经被构造,分别在锁之前和锁之后


如果要你撸代码,你就撸最后这一段,完美~~~面试官要是个女的准想和你生猴子。。。


哦,别高兴太早了,面试官要是个男的,也可能会问你下列问题


0.为何要检测两次?


如上面所述,有可能延迟加载或者缓存原因,造成构造多个实例,违反了单例的初衷。


1.构造函数能否公有化?


不行,单例类的构造函数必须私有化,单例类不能被实例化,单例实例只能静态调用


2.lock住的对象为什么要是object对象,可以是int吗?


不行,锁住的必须是个引用类型。如果锁值类型,每个不同的线程在声明的时候值类型变量的地址都不一样,那么上个线程锁住的东西下个线程进来会认为根本没锁,相当于每次都锁了不同的门,没有任何卵用。而引用类型的变量地址是相同的,每个线程进来判断锁多想是否被锁的时候都是判断同一个地址,相当于是锁在通一扇门,起到了锁的作用。


好了,这下估计男的都想跟你生猴子了。。。




觉得本文对你有帮助?请分享给更多人

关注「伯乐在线」

看更多精选 IT 职场文章


作者简介 ( 点击 → 加入专栏作者 )

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多