现在的位置: 首页 > 综合 > 正文

设计模式之五:单例模式

2013年10月05日 ⁄ 综合 ⁄ 共 2156字 ⁄ 字号 评论关闭

                     《Head First设计模式》第五章学习笔记

一、单例模式

    单例模式:确保一个类只有一个实例,并提供一个全局访问点。
    在java中实现单例模式,需要:私有的构造器、一个静态变量和一个静态方法。类图如下:

二、单例模式实现

  实现方式一:不考虑多线程情况

public class Singleton {
    private static Singleton uniqueInstance;
    private Singleton() {
    }
    public static Singleton getInstance() {
        if (uniqueInstance == null) {
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
}

该实现方式,在多线程的情况下,存在问题:会产生多个实例。
实现方式二:考虑多线程,但牺牲了性能

public class Singleton {
    private static Singleton uniqueInstance;
    private Singleton() {
    }
    public static synchronized Singleton getInstance() {
        if (uniqueInstance == null) {
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
}

跟实现方式一相比,getInstance方法使用synchronized进行了同步。这解决了多线程并发的问题,但是却因为同步而导致每次访问该方法,都付出了性能的代价(同步一个方法可能造成程序执行效率下降100倍)。

当然,如果getInstance()的性能对应用程序不是很关键(例如访问次数很少),则可以采用这种方式,既简单又有效。如果性能是关键(例如该方法被频繁调用),则需要使用下面的两种方式来实现。

实现方式三:使用“急切”创建实例,而不用延迟实例化的做法(也称为“饿汉式”单例类,与其对应的是“懒汉式”单例类)

public class Singleton {
    private static Singleton uniqueInstance = new Singleton();
    private Singleton() {
    }
    public static Singleton getInstance() {
        return uniqueInstance;
    }
}

利用这个做法,我们依赖JVM在加载这个类时马上创建此唯一的实例。其缺点就是,没有延迟实例化,只要类被加载了,就创建实例,而不管后续是否使用该实例。

实现方式四:用“双重检查加锁”,在getInstance()中减少使用同步

利用双重检查加锁,首先检查实例是否已经创建,如果未创建,则进行同步,然后再次判断是否已创建实例,如果没有,则创建。

public class Singleton {
    private volatile static Singleton uniqueInstance;
    private Singleton() {
    }
    public static Singleton getInstance() {
        if (uniqueInstance == null) {
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

uniqueInstance,定义成volatile,确保实例化的过程中,多线程能够正确的处理该变量。

注意:双重检查加锁不适用于jdk 1.4及更早的版本。


三、JDK中单例模式的应用例子

JDK中的java.lang.Runtime类,就是使用单例模式,而且使用的是上面介绍的第三种实现方式。如下:

public class Runtime {
    private static Runtime currentRuntime = new Runtime();

    /**
     * Returns the runtime object associated with the current Java application.
     * Most of the methods of class <code>Runtime</code> are instance 
     * methods and must be invoked with respect to the current runtime object. 
     * 
     * @return  the <code>Runtime</code> object associated with the current
     *          Java application.
     */
    public static Runtime getRuntime() { 
	return currentRuntime;
    }

    /** Don't let anyone else instantiate this class */
    private Runtime() {}
    
    // 其他方法省略
}

四、小结

1、如果有两个及以上的类加载器,加载Singleton类,仍然会导致Singleton类被实例化出多个对象。所以,如果你的程序有多个类加载器,同时你又使用了单例模式,则需要小心这个问题。
2、单例模式是针对同一个JVM而言的,对于不同的JVM,每个JVM都会创建一个对应的实例。

抱歉!评论已关闭.