Android 设计模式 - 单例模式

作者: Yink_Liu | 来源:发表于2018-08-21 11:51 被阅读31次

    Android 设计模式demo索引 Android 设计模式demo项目
    GitHub地址android-design-pattern欢迎fork/star

    前言

    单例模式是运用最广泛的设计模式之一,在应用这个模式时,单例模式的类必须保证只有一个实例存在。多用于整个程序只需要有一个实例,通常很消耗资源的类,比如线程池,缓存,网络请求,IO操作,访问数据库等。由于类比较耗资源,所以没必要让它构造多个实例,这种就是单例模式比较好的使用场景。

    单例模式定义

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

    单例模式举例

    1、饿汉式

    public class SingletionStarving {
    
        private static final SingletionStarving mInstance = new SingletionStarving();
    
        private SingletionStarving() {
    
        }
    
        public static SingletionStarving getInstance() {
            return mInstance;
        }
    }
    

    1、声明静态对象时就初始化
    2、static关键字修饰,静态变量,存储在内存中,只有一份数据。
    3、final关键字,只初始化一次,所以mInstance实例只有一个。

    2、懒汉式

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

    1、使用的时候即调用getInstance的时候才初始化
    2、static关键字修饰,静态变量,存储在内存中,只有一份数据。
    3、synchronized线程安全,多线程情况下单例的唯一性
    4、缺点:没次调用getInstance都会同步一次,浪费资源

    3、DCL式(Double Check Lock)

    网上建议和使用最多的方法

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

    1、使用的时候即调用getInstance的时候才初始化
    2、static关键字修饰,静态变量,存储在内存中,只有一份数据
    3、synchronized线程安全,多线程情况下单例的唯一性
    4、两次判断空,避免多次同步(synchronized)
    缺点
    private static SingletionDLC mInstance;
    private SingletionDLC() {}
    public static SingletionDLC getmInstance() {}
    由于jvm特性,允许乱序执行,上面三句代码顺序不定,那么就可能出现DCL失效的问题。
    步骤一、倘若A线程执行getmInstance(),还没执行构造方法SingletionDLC()
    步骤二、此时B线程调用getmInstance()。因为A已经执行getmInstance(),所以mInstance不为空就直接获取。
    步骤三、由于B直接获取,而真实情况是A线程构造方法还未执行,所以mInstance就为空了。
    虽然此情况发生概率较小,但也是一种情况。为了解决这种情况,java1.6开始加入volatile关键字

    private volatile static SingletionDLC mInstance;
    

    这样就避免了DCL方式失效的情况。虽然会volatile消耗一些性能,所以DCL最佳写法

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

    虽然volatile让DCL方式完美,但是没有volatile关键字的写法基本能满足绝大部分情况。除非你要运行在高并发,或者java1.6之前的代码中。

    4、静态内部类方式

    推荐使用

    public class SingletionInternalClass {
    
        private SingletionInternalClass() {}
    
        public static SingletionInternalClass getInstance() {
            return SingletionInternalClassHolder.instance;
        }
    
        private static class SingletionInternalClassHolder {
            private static final SingletionInternalClass instance = new SingletionInternalClass();
        }
    }
    

    1、使用的时候即调用getInstance的时候才初始化
    2、调用getInstance才回去加载SingletionInternalClassHolder类,确保了线程安全,保证了单例的唯一性

    5、枚举单例

    public enum  SingletionEmum {
        INSTANCE;
        public void dosomthing() {
            
        }
    }
    

    枚举在jva中和普通的类一样,可以有字段和自己的方法。枚举实例的创建时线程安全并且任何情况下它都是一个单例。包括反序列化的时候。

    总结

    单例模式不管用那种方式实现,核心思想都相同
    1、构造函数私有化,通过一次静态方法获取一个唯一实例
    2、线程安全
    本文代码android-design-pattern
    最后推荐使用文中DCL方式静态内部类的方式来创建单例模式。

    相关文章

      网友评论

        本文标题:Android 设计模式 - 单例模式

        本文链接:https://www.haomeiwen.com/subject/unssgttx.html