单例模式--GOF的23个之一

单例模式--GOF的23个之一


前言:部分来自《Head First 设计模式》,不涉及任何商务往来,仅为学习使用,作为参照笔记。单例模式确保一个类只有一个实例,并提供一个全局访问点。

 

单例模式(Singleton Pattern)

类图:

单例模式--GOF的23个之一

选择单例模式就是为了避免不一致状态。

优势:

1、   单例模式的类图是所有模式的类图中最简单的。

2、   单例模式让我们在任何时刻都只有一个对象。

3、   单例模式可以延迟实例化。

 

一、  用来创建独一无二的,只能有一个实例的对象的入场券。

单例模式

**可程序员约定==麻烦不言而喻。

**Java的静态变量控制==必须一开始就创建,创建非常耗资源且可能不用而浪费。

 

二、  如何保证一个对象只能被实例化一次?

这其实和实现有关。有些JVM的实现是:在用到的时候才创建对象。

附:如果我们不需要这个实例,他就永远不会产生。这就是“延迟加载”(Lazy Instantiaze)。

 

三、  单例模式的作用:常常被用来管理共享的资源。

 

四、单例模式的特征:

1、没有公开的构造器。

2、想要取得实例,必须以“请求”得到一个实例的方式,而不是自行实例化得到一个实例。类有一个静态方法,叫做getInstance()。调用这个方法,就立刻得到对象,随时可以工作。

附:事实上,对象可能是在这次调用的时候被创建出来的,也可能是以前早就被创建出来的。

 

五、单例模式中的两个原理

1、把某个类设计成自己管理的一个单独实例,同时也避免其他类再自行产生实例。要想取得单例实例,通过单例类是唯一途径。

2、提供对这个实例的全局访问点:当需要实例时,向类查询,它会返回单个实例。前面的例子利用延迟实例化的方式创建单件,这种做法对资源敏感的对象特别重要。

 

六、多线程与单例模式

1、困难:有多个线程都要执行这段代码。

解决:只要把getInstance()变成同步(synchronized)方法,多线程灾难几乎就可以轻易解决了。(后面“饿汉式单例模式“)

2、新困难:同步会使性能降低。

解决:这个问题更严重,只有第一次执行此方法时,才真正需要同步。换句话说,一旦设置好uniqueInstance变量,就不在需要同步这个方法了。之后u每次调用这个方法,同步都是一种累赘。(后面“双重检查加锁单例模式”)

 

七、静态带来:类的单件与对象的单件问题。

除非有绝对的必要使用类的单件,否则还是建议使用对象的单件。

 

八、如果程序有多个加载器又同时使用了单件模式,就会产生多个单件并存的怪异现象。

解决:自行指定类加载器,并指定同一个类加载器。

 

九、“一个类,一个责任”原则在单例模式被打破。

这里,单件类不只负责管理自己的实例(并提供全局访问),还在应用程序中担当角色,所以也可以被视为两个责任。但是单例模式在整个程序设计中本身就用得不多,破坏这个原则也是可以原谅的。

 

十、想要子类能工作顺利,基类必须实现注册表(Registry)功能。

1、构造函数私有不能继承。

2、构造函数保护或公共,不能保持单例模式。

(后面“登记式单例模式”)

 

十一、为何全局变量比单例模式差(其实前面已经谈到一部分)。

这个模式的目的:确保只有一个实例并提供全局访问。全局变量可以提供全局访问,但是不能确保是有一个实例。全局变量也会变相鼓励开发人员,用许多全局变量指向许多小对象来造成命名空间(namespace)的污染。单例模式不鼓励这样的现象,但单例模式仍然可能被滥用。

 

十二、要点补充回顾

1、   在java中实现单例模式需要私有的构造器、一个静态方法和一个静态变量。

2、   如果不是采用第五版的Java2,双重检查加锁实现会失效。

3、   如果使用JVM1.2或之前版本,唏嘘建立单例注册表,以免垃圾收集器将单例回收。

 

下面是经典模式和四种使用模式

经典模式代码:

/**

 * 传统模式(经典模式)。

 * 不是线程安全的。

 */

public class Singleton {

    private static Singleton uniqueInstance;

 

    // 这里写其他有用的变量。

 

    private Singleton() {}

 

    public static Singleton getInstance() {

       if (uniqueInstance == null) {

           uniqueInstance = new Singleton();

       }

       return uniqueInstance;

    }

 

    // 这里写其他有用的方法。

}

 

方法一 懒汉式单例模式

简介:在第一次调用的时候实例化。如果getInstance()的性能对应用程序不是很关键,就可以用这种方式。

/**

 * 方法一  懒汉式单例模式。

 * 基本解决线程问题,但是性能降低。

 */

publicclass Singleton {

    private static Singleton uniqueInstance;

 

    // 这里发其他有用的变量。

 

    private Singleton() {}

 

    public static synchronized SingletongetInstance() {

        if (uniqueInstance == null) {

            uniqueInstance = new Singleton();

        }

        return uniqueInstance;

    }

 

    // 这里放其他有用的方法。

}

 

方法二 饿汉式单例模式。

简介:在类初始化时,已经自行实例化。使用“急切”创建实例,而不用延迟实例化的做法。如果应用程序总是创建并使用单例模式,或者在创建和运行时方面的负担不太繁重,就可能想要急切(eagerly)创建单例。

/**

 * 方法二 饿汉式单例模式。

 *

 */

publicclass Singleton {

    private static Singleton uniqueInstance =new Singleton();

 

    private Singleton() {}

 

    public static Singleton getInstance() {

        return uniqueInstance;

    }

}

 

方法三 双重检查加锁单例模式。

简介:懒汉式+提高性能。用“双重检查加锁”,在getInstance()中减少使用同步。利用双重检查加锁(double-ckecked locking),首先检查是否实例已经创建了,如果尚未创建,“才”进行同步。这样一来,只有第一次同步,这已是我们想要的。

附:volatile关键词确保,当uniqueInstance变量被充实化成Singleton实例时,多个线程正确处理uniqInstance变量。

注意:如果性能是关心的重点,那么这个做法可以大大地减少getInstance()的时间耗费。同时,双重检查加锁不低于用1.4级更早版本的Java。

/**

 * 方法三 双重检查加锁单例模式。

 * 懒汉式+提高性能。

 *

 * 危险:这种模式采用第五版的Java2及以前会失效。

 */

publicclass Singleton {

    private volatile static SingletonuniqueInstance;

 

    private Singleton() {}

 

    public static Singleton getInstance() {

        if (uniqueInstance == null) {

            synchronized (Singleton.class) {

                if (uniqueInstance == null) {

                    uniqueInstance = newSingleton();

                }

            }

        }

        return uniqueInstance;

    }

}

 

方法四 登记式单例模式

简介:常用来给用子类的单例类。也叫注册表式。

注意:JVM1.2即以前版本,本方案不起作用。

importjava.util.HashMap;

importjava.util.Map;

 

/**

 * 方法四  登记式单例模式。

 * 警告:使用JVM1.2及之前的版本,本方案不起作用。

 */

publicclass Singleton {

    private static Map<String, Singleton> map= new HashMap<String, Singleton>();

 

    static{

        Singleton singleton = new Singleton();

        map.put(singleton.getClass().getName(),singleton);

    }

   

    protected Singleton(){}

   

    public static Singleton getInstance(Stringname){

        if(name==null){

            name = Singleton.class.getName();

        }

        if(map.get(name)==null){

            try{

                map.put(name,(Singleton)Class.forName(name).newInstance());

            }catch(InstantiationException e){

                e.printStackTrace();

            }catch(IllegalAccessException e){

                e.printStackTrace();

            }catch(ClassNotFoundExceptione){

                e.printStackTrace();

            }

        }

        return map.get(name);

    }

 

}

 

 

如有好的建议,可留言或发至笔者邮箱:fzb_xxzy@163.com

 

 

单例模式--GOF的23个之一

上一篇:UVALive - 2963 Hypertransmission


下一篇:11.2.0.4 Patch Set - Availability and Known Issues (Doc ID 1562139.1)