单例模式 (Singleton)

2018-10-14  本文已影响4人  kinglong1984

本文主要根据以下著作中的内容整理而来:

何红辉,关爱民.Android源码设计模式解析与实践第2版〔M〕.北京:人民邮电出版社,2017.7

定义

确保某一个类只有一个实例,自行实例化并向整个系统提供这个实例。

角色

Client——高层客户端。

Singleton——单例类。

特点

私有:构造方法是私有的,静态变量是私有的。

静态:通过一个公有的静态方法或枚举返回单例类对象。

线程安全:多线程环境下也要确保对象只有一个。

几种实现方式

以下几种方式在初始化静态对象上有不同。

饿汉模式:声明静态对象时直接初始化。new的过程很耗时的话,应用程序启动的会很慢,降低体验的友好性

懒汉模式:声明一个静态对象,在用户第一次调用getInstance()时初始化。延迟单例的实例化。

                懒汉模式 问题:getInstance的调用频率很高的话,每次都synchronized同步访问,效率低。

双重检查锁定(DCL):既能在需要时初始化,又能保证线程安全,消除懒汉模式中重复同步问题。(得比较多的方式,JDK1.5及以上版本使用volatile,没有复杂的并发场景下一般能满足使用)

静态内部类:解决双重检查锁定失效问题。(推荐使用的实现方式)

代码示例

双重检查锁定方式:

public class Singleton{

    //问题1:new操作的反汇编代码,其实他包含3条汇编指令:new、dup、init。sInstance 不为null并不保证sInstance是完整地初始化好了成员。

    // 问题2:即使一个线程实例化了sInstance ,由于每个线程都有自己的working缓存,可能另一个线程看不到前一个线程对sInstance 的操作。

    // JDK1.5及以后 使用volatile关键字,保证每次sInstance都是从主内存中获取。

    private volatile static Single sInstance = null;

    private Singleton(){}

    public static Singleton getInstance(){

        if(sInstance==null){

            synchronize(Singleton.class){

                if(sInstance==null){

                       sInstance = new Singleton();//实际上不是一个原子操作:1.给Singleton实例分配内存;2.调用Singleton()构造方法初始化成员字段;3.将sInstance指向分配的内存空间。 执行顺序可能是1-2-3或者1-2-3。

                }

            }

        }

    }

}

静态内部类方式:

public class Singleton{

    private Singleton(){}

    public static Singleton getInstance(){

        // 延迟初始化

        return SingletonHolder.sInstance;

    }

   private static class SingletonHolder{

        //静态变量是线程共享的,保证了单例的线程安全。避免了双重检查锁定失效问题。

        private static final Singleton sInstance = new Singleton();

    }

}

扩展应用

使用容器实现单例

Android Context的实现类ContextImpl

public class ContextImpl extends Context {

    private final static Map<String,ServiceFetcher> SYSTEM_SERVICE_MAP= new HashMap<String,ServiceFetcher>();

    public static void registerService(String serviceName,ServiceFetcher fetcher){

        //省略代码

        SYSTEM_SERVICE_MAP.put(serviceName,fetcher);

    }

    public Object getSystemService(String key){

        ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(key);

        return fetcher ==null? null:fetcher.getService(this)

    }

    static {

        registerService(LAYOUT_INFLATER_SERVICE,new ServiceFetcher(){

            public Object createService(ContextImpl ctx){

                return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());

            }

        })

    }

}

上一篇下一篇

猜你喜欢

热点阅读