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

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

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

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

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

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

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

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

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

 

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

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

一.私有构造函数

二.声明静态单例对象

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

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

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

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

0.为何要检测两次?

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

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

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

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

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

 

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

打赏支持我写出更多好文章,谢谢!

打赏作者

打赏支持我写出更多好文章,谢谢!

任选一种支付方式

5 17 收藏 25 评论

关于作者:熊绎

徘徊于技术与管理的岔路口,希望为新人提供一些帮助 个人主页 · 我的文章 · 82 ·     

相关文章

可能感兴趣的话题



直接登录
最新评论
  • theboboy   01/04

    面试被问设计模式,分怎么问法,如果单纯的问某个设计模式的概念,用法,我会选择直接pass,如果问设计某个东西可以用到什么设计模式,我觉得是有意义的。

    我觉得设计模式四个字中,重要的是设计,而不是模式。没模式但会设计,也没问题。相反,会模式,不会设计,就像是会画电路图,不会换灯泡。

    • 熊绎 IT solution 01/04

      一般面试官会问你有没有用过/熟悉/了解/听说过设计模式

      你说有

      面试官会问你用过/熟悉/了解/听说过哪个设计模式

      你说xxxx模式

      面试官会开始让你展开讲一下你的理解或者你是如何在项目中使用的

      然后就开始讲了

       

      当然也有比较low的面试官直接问某一个设计模式,但是除了真不会,也不能直接pass啊。。。

      • theboboy   01/05

        是的 我们遇到的就是你说的这两种

        我说的直接pass  意思是可以不考虑这家公司了  我觉得面试官是可以体现出公司的技术水平的

        一般按照上面问的话  我通常会说JDK源码中用了哪些设计模式

        如果非让细抠  我觉得就没什么意思了  当然不是觉得设计模式不好  而是觉得重要但不是必要的  实际工作中更重要的是合理的解决问题

  • sf705   01/04

    外面的if 可以去掉吧?

    • 熊绎 IT solution 01/04

      双重检测,不可以去的哦~~~仔细想想为什么不能去

      • 迈克~! 软件开发 01/17

        是出于性能的考虑吧

        • 熊绎 IT solution 01/17

          对,否则每个线程进去都要执行到lock那里,判断一下被锁住的对象是否被释放

        • sf705   02/13

          嗯嗯 觉得是出于性能考虑的,不然只需要lock里面一个if判断就可以了,毕竟加锁比较耗时

  • cologler   01/05

    我比较喜欢用 Interlocked.CompareExchange 而不是 lock,如果对象是可重 new 的话。

  • 南波万 Android 01/09

    你好

    是否需要添加 volatile 关键字

    • 熊绎 IT solution 01/09

      一般不用加。

      volatile我的理解是每个线程运行到这里,读取有volatile关键字的变量的时候都是从内存中取而不是读取缓存,让每个线程读取这个变量的时候都是最新的。但是一般来说单例的实例都是偏重于一些行为的,也就是说要做一些事情的,而不会存值,所以如果只是需要单例实例执行一些动作,是不用加volatile的。

  • 414776908   01/14

    您好,作者,非常喜欢您这篇文章,给我很大的启发,只是我有两个疑问,想请教你一下,第一,构造函数用了private关键字,虽然可以防止继承,但是却不能防止克隆,如果用clone,也能得到不同对象。第二个,我在网上查了一下lock这个方法,没有查到,我就理解为同数据库锁表一样的,据我了解内存分为栈空间,堆空间,一般像整型,字符串这种大小固定,又比较小的数据存在栈空间里面,而对象是存在堆空间的,lock(obj)是不是就锁定堆空间整个内存片段,如果有其他的对象存在堆空间里面,是不是在锁定的这个时候就不能修改,也不能销毁,这样会不会对其他对象造成一定的影响。因为我对lock函数不是很了解,如果有说错的地方,请您忽怪。以上是我的疑问,想请教你一下。

    • 熊绎 IT solution 01/16

      一、关于clone的问题。

      实际上能产生新对象有4种办法:new 、克隆、序列化、反射

      private修饰构造函数,所以new被杜绝了。而你提到的克隆,如果需要对一个对象调用克隆方法,这个对象必须实现了ICloneable(好像是这个)接口,否则会报错。好,即便你实现了ICloneable接口,你可以克隆一个单例对象,克隆对象是不会执行构造函数的,而是直接从内存中复制的。所以单例用克隆是没法创造新的实例的。而序列化和反射都有防止破坏单例的机制,你可以自己查查看。

      二、lock方法

      这里我代码是用的C#,lock是C#的关键字。如果用java的话,可以用一个synchronized关键字修饰一下,表示是个同步方法,其实作用差不多,还有其他的办法实现线程同步,我就不一一举例了。lock住的话表示括号里面对象的地址被第一个访问到这里的线程锁定,如果下一个线程访问到这里,会检测该对象是否被释放,如果没有被释放,则等待,如果已经被释放,则锁定然后再继续往后走。这里所谓锁定该对象实际是锁定了该对象所对应的地址引用。

      三、值类型与引用类型

      数据存在栈或者堆,不是由“大小”来决定,而是由“类型”决定,无论大小,int,float,enum,structure等等这些类型都是值类型,是直接把变量值存在栈中,而object,class,list,等等这些类型都是引用类型,是把变量内存地址存在栈中,把值存在堆中。需要注意的是,在某些语言中(例如C#), 由于string使用太频繁,所以把string已经封装成为一种安全类型,即在使用中已经没有引用类型的特性,好像跟值类型一样,就是直接把值存到栈中,但是string依然是引用类型。

  • 迈克~! 软件开发 01/17

    lock方法是API中还是博主自己写的?直接用synchronized不就行了

  • 男人泪 程序猿 01/25

    单例模式有5种写法,不同写法有不同的应用,不能说哪种写法是对的

  • 返本归猿 程序猿 02/09

    私有构造、delete拷贝构造、delete operator=;

    最简单的使用静态变量,最安全的需要使用原子操作

  • liweijun Java工程师 1 天前

    请问这个是系列文章么?如何看你写的其他文章呢?

跳到底部
返回顶部