设计模式(003) 单例模式[上] 单身懒汉

 设计模式(003) 单例模式[上] 单身懒汉

什么是单例模式(What)?

   GOF:“保证一个类仅有一个实例,并且提供一个访问它的全局访问点”。

“嗯...,GOF通常一言九鼎,单例就是这样子的。” -- OO先生边思考边说。

YSJIAN  :“等等,我插一句,保证应用中一个类最多只有一个实例存在,并提供一个全局访问点访问它”。

只见OO先生会心一笑。

为什么用单例模式(Why)?

从What中貌似一目了然了,GOFYSJIAN说的都是为了控制类的实例个数,表面上看YSJIAN的插话只是在重复GOF的描述,喜欢“咬文嚼字”的OO先生发现了这其中的玄机。首先回答一个问题:由谁类控制类的实例个数呢?

有人说:单利要多见简单有多简单,public static final INSTANCE = new Instance();你要用我的实例,Instance.INSTANCE拿走即可。的确,你发了一条广播出去,接收到的人会乐意按照你说的方法去使用,而不知道的人呢,更有new党,一上来就来一个new Instance(),然后到处开花,悲剧了。

怎么办?让Instance自己来控制实例的个数,而不是交给使用者,正所谓将邪恶扼杀于源头,可避免是是非非。当然这就需要Instance多干点活了。也没关系,一次搞定,服务持久,何乐而不为。

单例模式在哪里用(Where)?

我要说单利用在需要它的地方就有点zuo了。这么讲吧,如果这个类只需要一个实例即可满足我们的业务需求,或者它的实例一旦多余一个会出问题。比如说中国,China这个类如果不是单例就有点不合适了。一个工厂的中央炼钢炉,涉及到钢水,水,温度,气压这些危险地东西,一旦某个数值有差错就可能造成生产事故,这个时候炼钢炉存在多个实例就是万万不允许的,或者是有多个线程去操作非同步的它也是不合理的。

如何运用单例模式(How)?

单例,毫不夸张地说,是个开发者都能随手画出一个,赶紧上图:

public class LazyUnsafeSingleton {

   private volatile static LazyUnsafeSingleton instance;

   private LazyUnsafeSingleton() {
   }

   public static LazyUnsafeSingleton getInstance() {
      if (null == instance) {
         instance = new LazyUnsafeSingleton();
      }
      return instance;
   }
}

貌似这个就可能出现问题,当多线程操作的时候,难保它能搞出几个实例来,于是YSJIAN加了几行代码,使用了第一招双重校验加同步:

public class LazyDoubleCheckSingleton {

   private volatile static LazyDoubleCheckSingleton instance;

   private LazyDoubleCheckSingleton() {
      Logger.getGlobal().info("LazyDoubleCheckSingleton.LazyDoubleCheckSingleton()");
   }

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

     OO先生若有所思头地看着,边点头边吐出了一句:”内部类实例持有者可以让代码再简洁点...”,几分钟后,JSJIAN又上一副新图:

public class LazyHolderSingleton {

   private static class InstanceHolder {
      private static final LazyHolderSingleton INSTANCE = new LazyHolderSingleton();
   }

   private LazyHolderSingleton() {
      Logger.getGlobal().info("LazyHolderSingleton.LazyHOlderSingleton()");
   }

   public static LazyHolderSingleton getInstance() {
      return InstanceHolder.INSTANCE;
   }
}

细心的你已经发现了什么了?这几个单例,类名都是以Lazy为前缀。不用查找翻译啦,Lazy就是说这几单例很懒,没事就在那睡懒觉,什么时候叫他了(invoke:调用),他就醒了,并开始为你效劳。

第一种,线程不安全;第二种,解决了线程安全问题。第三种,JVM类加载机制决定了它的线程安全。第四种…有第四种吗?这个可以有,也可以没有,不卖关子了,肯能你已经想到了,且看图:

public class LazyMethodSynSingleton {

    private static LazyMethodSynSingleton instance;

    private LazyMethodSynSingleton() {
        Logger.getGlobal().info("LazyMethodSynSingleton.LazyMethodSynSingleton()");
    }

    public static synchronized LazyMethodSynSingleton getInstance() {
        if (null == instance) {
            instance = new LazyMethodSynSingleton();
        }
        return instance;
    }
}

不出意外,这个也是Lazy的,无非就是将双重校验加同步中的同步加到方法上了,我称之为方法同步。仔细揣摩两者的区别,不难发现:

双重校验加同步:只有Instace为null的时候加锁,而且这把锁几乎不用去管它。

方法同步:不论Instance是否为null,先加把锁再说,而且每次调用都会加锁。

对比完两者的区别,我想之前使用方法同步的同学就要赶紧转移阵地了,如果执意不想转的同学,试问一下:一个99%的情况下不需要加锁的,你去给加了把锁,是不是浪费很多不必要的时间,不合适,对吧,太伤感情,JVM会生气的!

到这里,懒汉式单例经典实现就是上面的四种了,第一种,因为无意识而没有安全感,第四种因为对危险充满恐惧,徒耗精力。选项貌似一下子砍了50%,至于使用哪种,随心,随性就好,我遵循代码简洁之道(此处并不是特指Robert C. Martin的Clean Code),选择了内部类实例持有者

懒汉子即将返璞归真,退出舞台,且看下个回合饿汉子如何搔首弄姿。

PS : OO先生,YSJIAN,GOF,等加粗斜体的表示人名,详细内容欢迎回顾《开启设计之旅》。亲爱的同仁,若有疑问,欢迎留言交流。

其他参考:

设计模式(001) 开启设计之旅

设计模式(002) 模式语录

时间: 2024-10-16 09:35:05

设计模式(003) 单例模式[上] 单身懒汉的相关文章

抽象类 抽象方法 接口 类部类 匿名类部类 设计模式之单例模式(懒汉模式及饿汉模式)

---恢复内容开始--- 抽象类  关键字  abstract 不能被实例化(创建对象),可通过类名调用静态方法 子类继承抽象类必须重写父类的所有抽象方法,然后用多态调用 接口:关键字 interface   类名 implements 接口名 1.接口中只能有抽象方法,并且不能被实例化,通过多态调用 2.接口与接口之间的关系: 继承关系(可以多继承); 类部类: 在类中定义的类 创建类部类对象    外部类名.内部类名  对象名 = new 外部类名().new内部类名() 匿名类部类: 在写

Java设计模式:单例模式

概念: java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建自己的唯一实例. 3.单例类必须给所有其他对象提供这一实例. 单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例.在计算机系统中,线程池.缓存.日志对象.对话框.打印机.显卡的驱动程序对象常被设计成单例.这些应用都或多或少具有资源管理器的功能.每台计算机可以有若干个打印机,但只能

[转]JAVA设计模式之单例模式

原文地址:http://blog.csdn.net/jason0539/article/details/23297037 概念: java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建自己的唯一实例. 3.单例类必须给所有其他对象提供这一实例. 单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例.在计算机系统中,线程池.缓存.日志对象.对话

设计模式实例学习-单例模式(Android中的使用场景)

1.设计模式实例-单例模式 单例模式,故名思议,是指在一个类中通过设置静态使得其仅创造一个唯一的实例.这样设置的目的是满足开发者的希望--这个类只需要被实例化创建一次,同时因为其为静态的缘故,加载的速度也应该快于正常实例化一个类的速度(理论上). 在Android开发中,当我们需要创建一个Fragment的时候常常会用到这样的模式,没有代码的学习是虚无的,接下来亮代码学习: public class SelectFrame extends Fragment { private final sta

Java 设计模式(3)单例模式

前言 概念: java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建自己的唯一实例. 3.单例类必须给所有其他对象提供这一实例. 单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例.在计算机系统中,线程池.缓存.日志对象.对话框.打印机.显卡的驱动程序对象常被设计成单例.这些应用都或多或少具有资源管理器的功能.每台计算机可以有若干个打印机,

设计模式(4)单例模式

概念: Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建自己的唯一实例. 3.单例类必须给所有其他对象提供这一实例. 单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例.在计算机系统中,线程池.缓存.日志对象.对话框.打印机.显卡的驱动程序对象常被设计成单例.这些应用都或多或少具有资源管理器的功能.每台计算机可以有若干个打印机,但只能

JAVA设计模式之单例模式(转)

本文继续介绍23种设计模式系列之单例模式. 概念: java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建自己的唯一实例. 3.单例类必须给所有其他对象提供这一实例. 单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例.在计算机系统中,线程池.缓存.日志对象.对话框.打印机.显卡的驱动程序对象常被设计成单例.这些应用都或多或少具有资源管理器

软件设计模式之单例模式

什么是单例模式? 单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例类的特殊类.通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源.如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案. 单例模式的特点: 1.单例类有且只能有一个实例. 2.单例类需要自己创建一个自己的实例. 3.单例类需要为其他类提供这个实例. 哪些地方经常用到单例? 在计算机系统中,配置文件,线程池,缓存,日志对象,打印机等经常用到单例

设计模式之单例模式——Singleton

                    设计模式之单例模式--Singleton 设计意图: 保证类仅有一个实例,并且可以供应用程序全局使用.为了保证这一点,就需要这个类自己创建自己的对象,并且对外有公开的调用方法.而且,别的类不能实例化它,所以构造方法要设置为私有的. 单例模式的要点 一是某个类只能有一个实例: 二是它必须自行创建这个实例: 三是它必须自行向整个系统提供这个实例. 例如: 有一个"单例对象",而"客户甲"."客户乙" 和&quo