【设计模式】单例模式

一、概念

单例模式是设计模式中最为简单和为人熟知的一种设计模式,属于创建型模式的一种。

定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

UML图表示法:

Singleton
-uniquenstance:Singleton

-Singleton():void

+getInstance():Singletion

+operation():void

  图1 单例模式结构图

单例模式具有以下几个特点:
①单例类只能有一个实例
②单例类必须自己创建自己的唯一实例
③单例类必须给所有其他对象提供这一实例

二、示例代码

1、懒汉式:

package com.qunar.base.collection;

public class Singleton {
    /** 定义一个私有的静态变量来存储创建的实例 **/
    private static Singleton uniqueInstance = null;

    /** 私有化构造方法,防止外部类来创建此类的实例 **/
    private Singleton() {
    }

    /**
     * 定义一个public的静态方法,向外界提供仅有的这个实例
     * 
     * @return
     */
    public static synchronized Singleton getInstance() {
        if (uniqueInstance == null) {
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }

    /**
     * 实例方法,外界只能通过仅有的实例来调用此方法
     */
    public void operate() {

    }

}

2、饿汉式

package com.qunar.base.collection;

public class Singleton {
    /** 定义一个私有的静态变量来存储创建的实例,直接在声明变量时创建此实例 **/
    private static Singleton uniqueInstance = new Singleton();

    /** 私有化构造方法,防止外部类来创建此类的实例 **/
    private Singleton() {
    }

    /**
     * 定义一个public的静态方法,向外界提供仅有的这个实例
     * 
     * @return
     */
    public static Singleton getInstance() {
        // 直接返回已经创建好的实例
        return uniqueInstance;
    }

    /**
     * 实例方法,外界只能通过仅有的实例来调用此方法
     */
    public void operate() {

    }

}

三、模式讲解

1、单例模式的优缺点

  a、时间和空间

  懒汉式是典型的时间换空间,而饿汉式是典型的空间换时间。

  b、线程安全

  不加同步的懒汉式是线程不安全的

  饿汉式是线程安全的

2、双重检查加锁方式实现的单例模式

  代码如下:

package com.qunar.base.collection;

public class Singleton {
    /** 对保存实例的变量添加volatile修饰 **/
    private volatile static Singleton uniqueInstance = null;

    /** 私有化构造方法,防止外部类来创建此类的实例 **/
    private Singleton() {
    }

    /**
     * 定义一个public的静态方法,向外界提供仅有的这个实例
     * 
     * @return
     */
    public static Singleton getInstance() {
        // 先检查实例是否存在,如果不存在,才会进入下面的同步块
        if (uniqueInstance == null) {
            // 同步快,线程安全的创建实例
            synchronized (Singleton.class) {
                // 再次检查是否存在,如果不存在才真正的创建实例
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }

    /**
     * 实例方法,外界只能通过仅有的实例来调用此方法
     */
    public void operate() {

    }

}

在上面的代码中,如果没有对变量uniqueInstance添加volatile修饰,则可能会产生以下问题:

out-of-order write问题(详见 DoubleCheckedLocking):

instance = new Singleton();  的顺序应该是

1 分配内存
2 构造函数初始化
3 将对象的reference赋值给instance

但因为Java Memory Model的问题,可能出现下面的所谓out-of-order write的问题:

1 分配内存
2 将对象的reference赋值给instance
3 构造函数初始化

也就是还没对对象初始化,就已经instance != null了,这样如果另外一个线程这时候对实例进行操作,可能有意想不到的结果。

四、单例模式的调用顺序示意图
        由于单例模式有两种实现方式,那么它的调用顺序也分成两种。先看懒汉式的调用顺序,如图2所示:


                        图2  懒汉式调用顺序示意图

饿汉式的调用顺序,如图3所示:


                               图3  饿汉式调用顺序示意图

五、应用场景

     在我们的应用中,有一个类AppConfig,用以读取配置文件中的配置信息,并在程序中需要配置信息时,被使用,由于配置信息是全局性的,因此在整个应用中,仅需要一个该类的实例,这样只需要读取一次配置文件,并且也会节省内存,避免不必要的内存浪费,此时就可以采用单例模式。

posted @ 2012-10-19 19:21  风*依旧  阅读(201)  评论(0编辑  收藏  举报