设计模式之五:单例模式

来源:互联网 发布:淘宝助理菜鸟模板设置 编辑:程序博客网 时间:2024/06/09 20:51

                     《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都会创建一个对应的实例。

原创粉丝点击