单例模式--需要计划生育的类

单例模式应该是应用最广的一个设计模式了,在以往的工作中,也经常遇到单例类,下面做个总结。

一 、为什么要用单例,或者说什么场景下要用单例

1.为了避免产生多个对象消耗过多的资源,例如访问IO和数据库等资源,这时要考虑使用单例模式
2.某种类型的对象应该只有一个,比如我们会写个Cache类,为了保持数据的统一,只能有一个类
3.需要频繁创建和销毁对象时,为了节省性能销毁,要使用单例

二、单例的几种常用写法

单例的写法有很多,饿汉模式,懒汉模式,枚举类方式等等,但是常用的有以下两种


1.DSL(Double Check Lock)双重校验锁方式

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

如果是JDK5,为了避免DCL失效问题(JDK1.5以前,JMM,Java Memory Model即Java内存模型,无法保证汇编指令的正确执行顺序),只需将mInstance的定义改成

 private volatile static Singleton  mInstance;

感兴趣的同学可以查一下volatile的作用。
所以正确的DCL写法应该如下:

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

这种DCL的方式,资源利用率高,第一次执行getInstance()时,单例对象才会初始化,但是第一次加载时,反应稍慢,由于java内存模型的原因偶尔会失败,这种方式可以在并发场景不是很复杂,或者JDK6或以后版本下使用。


2.静态内部类形式

public class Singleton {
    private Singleton(){}
    public static final Singleton getInsatance(){
        return SingletonHolder.mInstance;
    }

    private static class SingletonHolder{
        private static final Singleton mInstance = new Singleton();
    }
}

这种方式同样利用了classloder的机制(类加载机制)来保证初始化instance时只有一个线程,这种方式是Singleton类被装载了,instance不一定被初始化,因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会装载SingletonHolder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,那么就应该让他延迟加载。


3.枚举单例

public enum Singleton {
    INSTANCE;
    public void doSomething(){
    System.out.println("do sth.");
    }
}

写法简单是枚举单例最大的优点,枚举类和普通类一样,也有字段和方法。最重要的是默认枚举实例的创建是线程安全的,而且还能防止反序列化重新创建新的对象,所以在任何情况下它都是一个单例。

这种方式是Effective Java作者Josh Bloch 提倡的方式, 不过由于JDK1.5中才加入enum特性,用这种方式写不免让人感觉生疏,在实际工作中,我也很少看见有人这么写过。

总结:以上三种方式都可以使用,一般场景用DCL方式就行,用的最多的一般是静态内部类方式,想装逼的可以用枚举单例。

猜你喜欢

转载自blog.csdn.net/unicorn97/article/details/80358871
今日推荐