调侃《Head First 设计模式》之单例模式

来源:互联网 发布:大数据mobi 编辑:程序博客网 时间:2024/05/01 06:25

           对于一个类来说,平常我们可以随便new出无限多个对象(只要内存hold得住),但是像线程池、缓存、对话框、日志对象、设备驱动程序的对象只能有一个对象,如果制造多个实例就会出现问题。比如程序行为异常,资源使用过量等。

       那如何让一个类只能有一个对象呢?也许你想到了在另一个类持有该类的对象引用,在要new出这个类的时候判断下该引用是否为空就可以了。但这样做会提高类间的耦合度,而且会在不同类间出现重复代码。明明是你自己只能有一个实例这件事,却还得别人帮你做,这显然不好,所以要让类自己去让自己只可以有一个实例。所以我们只能从它的构造器入手。我们按照如下步骤思考:

       1.首先不能让其他的类随便调用它的构造器,怎么办呢?我们可以将它的构造器改为私有的(可以么?可以!)

       2.构造器编程私有,那就只能它自己的实例可以调用构造器了,但是调用构造器之前哪来的实例(鸡生蛋问题)?

       3.错了,可以使用静态方法去调用私有的构造器。剩下的就是调用前判断下对象是否存在了。如下图:

      

      


   这里uniqueInstance要定义为静态变量,这样它的生命周期和类的生命周期一样,而不是和实例变量一样生命周期是对象的生命周期。

   这样调用getInstance方法时先判断uniqueInstance是否为空,为空则创建对象,并将uniqueInstance指向该对象,方法返回该引用。下次getInstance被调用的时候,uniqueInstance已经不是空了,所以直接返回之前new出来的对象,这样每次调用getInstance都返回同一个对象。

   

   来看看官方对单例模式的定义:

   确保一个类只有一个实例,并提供一个全局访问点。



   故事好像到这里该结束了。no。这只是在单线程的情况下,一个类使用这种方式确实只可以产生一个对象,但是多线程情况呢?当两个线程同时访问getInstance方法时,uniqueInstance可都是空的哦。。

   不用怕,java对于多线程带来的资源使用问题早已经有办法。我们可以使用同步给getInstance方法加锁:

  

    看,当一个线程进入getInstance方法时,另一个线程只能在门等待。直到第一个线程执行完方法后,第二个线程进入getInstance方法时uniqueInstance已经不为空了。所以也得到了我们想要的结果。

    

    但是这样每次调用getInstance方法都要使用同步,这很消耗系统开销的哦。对此我们有三种不同的方案实现:

    1.假如对性能要求不高的应用程序来说,那么直接使用上面同步的方法就行了。


    2.如果对性能要求高的话,可以在类加载入虚拟机的时候就将对象new出来,像这样:

    


   这样确保在任何线程进入的时候就已经有对象了。

    

   3.使用“双重检查加锁”,只在对象不存在的时候使用同步一次。

     

    可能大家有疑问为什么有两次执行if(uniqueInstance == null)。原因是这样的,当第一次两个线程都通过第一个if(uniqueInstance == null)时,因为同步,只有一个线程可以进入synchronized后的代码,当第一个线程执行完方法后,第二个线程进入synchronized后的代码时如果不对uniqueInstance进行判断,那么线程照样会执行new对象的代码。

    

   




1 0
原创粉丝点击