Singleton Pattern单例模式

单例模式是一种对象创建模式,它用于产生一个对象的具体实例,它可以确保系统中一个类只产生一个实例。Java里面实现的单例是一个虚拟机的范围,因为装载类的功能是虚拟机的,所以一个虚拟机在通过自己的 ClassLoad 装载实现单例类的时候就会创建一个类的实例。在 Java语言中,这样的行为能带来两大好处:

  1. 对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销;

  2. 由于 new 操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻 GC 压力,缩短 GC 停顿时间。

因此对于系统的关键组件和被频繁使用的对象,使用单例模式可以有效地改善系统的性能。单例模式的核心在于通过一个接口返回唯一的对象实例。首要的问题就是要把创建实例的权限收回来,让类自身来负责自己类的实例的创建工作,然后由这个类来提供外部可以访问这个类实例的方法

单例模式基本实现

/**

 * 单例模式基本实现

 */

public class Singleton {



    // 首先单例模式必须要有一个private访问级别的构造函数,才能确保单例不会在系统中其他代码内被实例化,其次,instance 成员变量和

    // getInstance 方法必须是 static 的。

    private static Singleton instance = new Singleton();



    private Singleton() {

        

    }



    public static Singleton getInstance() {

        return instance;

    }

}

上述代码唯一的不足是无法对 instance 实例做延时加载,例如单例的创建过程很慢,而由于 instance 成员变量是 static 定义的,因此在 JVM加载单例类时,单例对象就会被建立,如果此时这个单例类在系统中还扮演其他角色,那么在任何使用这个单例类的地方都会初始化这个单例变量,而不管是否会被用到。

单例模式实验

public class Singleton {

    private Singleton() {

        System.out.println("Singleton is create");

    }



    private static Singleton instance = new Singleton();



    public static Singleton getInsatnce() {

        return instance;

    }

    public static void createString() {

        System.out.println("createString in Singleton");

    }

    public static void main(String[] args) {

        Singleton.createString();

    }

}

上述代码运行后的输出如

Singleton is create

createString in Singleton

可以看到,虽然此时并没有使用单例类,但它还是被创建出来,为了解决这类问题,需要引入延迟加载机制。

//延迟加载的单例模式

public class LazySingleton {

    private LazySingleton() {

        System.out.println("LazySingleton is create");

    }

    private static LazySingleton instance = null;



    public static synchronized LazySingleton getInstance() {

        if (instance == null) {

            instance = new LazySingleton();

        }

        return instance;

    }

    public static void createString() {

        System.out.println("create String");

    }



    public static void main(String[] args) {

        LazySingleton.createString();

    }

}

上述代码运行后的输出如

create String

上述代码首先对于静态成员变量 instance 初始化赋值 null,确保系统启动时没有额外的负载;其次,在 getInstance()工厂方法中,判断当前单例是否已经存在,若存在则返回,不存在则再建立单例。这里尤其要注意的是,getInstance() 方法必须是同步的,否则在多线程环境下,当线程1正新建单例时,完成赋值操作前,线程2可能判断instance为null,故线程2也将启动新建单例的程序,而导致多个实例被创建,故同步关键字是必须的。由于引入了同步关键字,导致多线程环境下耗时明显增加,两者测试代码如下

非同步的单例模式代码

public class Singleton implements Runnable{

    private Singleton() {

        System.out.println("Singleton is create");

    }



    private static Singleton instance = new Singleton();



    public static Singleton getInsatnce() {

        return instance;

    }

    public static void createString() {

        System.out.println("createString in Singleton");

    }

    public static void main(String[] args) {

        //Singleton.createString();

        for (int i = 0; i < 5; i++) {

            new  Thread(new Singleton()).start();

        }

    }

    @Override

    public void run() {

        long beginTime=System.currentTimeMillis();

        for (int i = 0; i < 100000000; i++) {

            Singleton.getInsatnce();

        }

        System.out.println(System.currentTimeMillis()-beginTime);

    }

}

代码运行后输出

Singleton is create

Singleton is create

Singleton is create

Singleton is create

Singleton is create

Singleton is create

1

0

1

2

0

完整的延迟加载方式代码

public class LazySingleton implements Runnable{

    private LazySingleton() {

        System.out.println("LazySingleton is create");

    }

    private static LazySingleton instance = null;



    public static synchronized LazySingleton getInstance() {

        if (instance == null) {

            instance = new LazySingleton();

        }

        return instance;

    }

    public static void createString() {

        System.out.println("create String");

    }



    public static void main(String[] args) {

        //LazySingleton.createString();

        for (int i = 0; i < 5; i++) {

            new  Thread(new LazySingleton()).start();

        }    

    }

    @Override

    public void run() {

        long beginTime=System.currentTimeMillis();

        for (int i = 0; i < 100000000; i++) {

            LazySingleton.getInstance();

        }

        System.out.println(System.currentTimeMillis()-beginTime);

    }

}

代码运行后输出

LazySingleton is create

LazySingleton is create

LazySingleton is create

LazySingleton is create

LazySingleton is create

LazySingleton is create

5125

5261

5270

5440

5454

为了解决同步关键字降低系统性能的缺陷,做了一定改进

//延迟加载的单例模式

public class LazySingleton implements Runnable{

    private LazySingleton() {

        System.out.println("LazySingleton is create");

    }

    private static LazySingleton instance = null;



    public static  LazySingleton getInstance() {

        if (instance == null) {

            synchronized (LazySingleton.class) {

                if (instance == null) {

                    instance = new LazySingleton();

                }

            }

        }

        return instance;

    }

    public static void createString() {

        System.out.println("create String");

    }



    public static void main(String[] args) {

        for (int i = 0; i < 5; i++) {

            new  Thread(new LazySingleton()).start();

        }    

    }

    @Override

    public void run() {

        long beginTime=System.currentTimeMillis();

        for (int i = 0; i < 100000000; i++) {

            LazySingleton.getInstance();

        }

        System.out.println(System.currentTimeMillis()-beginTime);

    }

}

首先判断instance是不是为null,如果为null,加锁初始化;如果不为null,直接返回instance。

/**

 * 解决同步关键字低效率

 *

 */

public class StaticSingleton {



    private StaticSingleton() {

        System.out.println("StaticSingleton is create");

    }



    private static class SingletonHolder {

        private static StaticSingleton instance = new StaticSingleton();

    }



    public static StaticSingleton getInstance() {

        return SingletonHolder.instance;

    }



}

单例模式使用内部类来维护单例的实例,当 StaticSingleton 被加载时,其内部类并不会被初始化,故可以确保当 StaticSingleton 类被载入 JVM 时,不会初始化单例类,而当 getInstance() 方法调用时,才会加载 SingletonHolder,从而初始化instance。同时,由于实例的建立是时在类加载时完成,故天生对多线程友好,getInstance() 方法也无需使用同步关键字。

登记式单例

  • //类似Spring里面的方法,将类名注册,下次从里面直接获取。  
public class Singleton3 {



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

    static {

        /*Singleton3 single = new Singleton3();

        map.put(single.getClass().getName(), single);*/

    }



    // 保护的默认构造子

    protected Singleton3() {

    }



    // 静态工厂方法,返还此类惟一的实例

    public static Object getInstance(String name) {

        if (name == null) {

            name = Singleton3.class.getName();

            System.out.println("name == null" + "--->name=" + name);

        }

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

            try {

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

            } catch (InstantiationException e) {

                e.printStackTrace();

            } catch (IllegalAccessException e) {

                e.printStackTrace();

            } catch (ClassNotFoundException e) {

                e.printStackTrace();

            }

        }

        return map.get(name);

    }



    public static void main(String[] args) {

        Singleton3 single3 =(Singleton3) Singleton3.getInstance("demo.pattern.singleton.Singleton3");

        Singleton3 single2 =(Singleton3) Singleton3.getInstance("demo.pattern.singleton.Singleton3");

        System.out.println(single2);

        System.out.println(single3);

    }



}

登记式单例实际上维护了一组单例类的实例,将这些实例存放在一个Map(登记薄)中,对于已经登记过的实例,则从Map直接返回,对于没有登记的,则先登记,然后返回。

参考资料

http://www.ibm.com/developerworks/cn/java/j-lo-Singleton/


你可能感兴趣的:(Singleton)