美文网首页
Android设计模式——单例模式

Android设计模式——单例模式

作者: 如愿以偿丶 | 来源:发表于2019-09-29 22:09 被阅读0次

1.单例设计模式

  单例设计模式就是应用程序中一个类只存在一个对象实例。常用的场景一般都是一些特殊的类,比如:用户管理类,工具类,Activity管理类,EventBus等等。

2.使用套路

  1. 构造方法私有化,防止在外部 new 对象
  2. 内部必须提供一个静态的方法,让其外部调用

3. 单例常用的几种方式

3.1 单例——饿汉式
  饿汉式特点:它在我们类加载的时候就进行创建对象

    /**
     * Created by ych on 2019/7/20.
     * Description:单例设计模式:只要保证应用程序中该类只存在一个实例
     *  单例套路:1.构造方法私有化(防止外部new 对象)
     *            2.提供一个静态方法,让其外部调用
     *  饿汉式特点:类一加载就会创建对象
     */
    public class SingletonHungry {

        private static SingletonHungry mInstance = new SingletonHungry();
    
        //1.构造函数私有化
        private SingletonHungry(){
    
        }
    
        public static SingletonHungry getInstance(){
            return mInstance;
        }
    }   

3.2 单例——懒汉式
  懒汉式特点:它和饿汉式不同,它是当我们需要的时候进行创建对象,更加高效

    /**
     * Created by ych on 2019/7/20.
     * Description: 懒汉式特点:当我们需要使用的时候才进行创建,更加高效
     */
    public class SingleLazy {
        private static SingleLazy mInstance;
    
        private SingleLazy(){
    
        }
    
        private static SingleLazy getInstance(){
            if (mInstance == null){
                mInstance = new SingleLazy();
            }
            return mInstance;
        }
    }

3.3 单例——懒汉式(DCL)(常用)
  DCL:即双重检查加锁(Double Check Lock)作用主要就是解决多线程并发问题,避免对象的多次创建。
  比如:多个线程都访问该对象,每个线程会抢占我们CPU时间片,当我们线程1获取到时间片,判断mInstance == null,此时线程2获取到CPU时间片,也进行判断mInstance == null,这样就会导致创建多个对象

    /**
     * Created by ych on 2019/7/20.
     * Description: 懒汉式,当我们使用的时候才会进行创建对象,可能更加高效
     * 但是存在一些问题,多线程并发问题,如果多线程调用还是会创建多个对象
     *   比如:多个线程都访问该对象,每个线程会抢占我们CPU时间片,当我们线程1获取到时间片,判断mInstance == null,此时线程2获取到CPU时间片,也进行判断mInstance == null,这样就会导致创建多个对象。
     *   解决:使用synchronized同步锁,但是注意需要双重验证。
     */
    public class SingleLazySync {
        private volatile static SingleLazySync mInstance;
    
        private SingleLazySync(){
    
        }
    
        /**
         * 既保证线程安全,效率还是比较高的
         * 注意:一定要在synchronized同步锁中再次进行判断,否则多线程下还是会创建多个对象。
         * @return
         */
        private static SingleLazySync getInstance(){
            if (mInstance == null){   // 线程1  线程2都在这里排队了,如果去掉synchronized中的if判断还是会创建多个对象
                synchronized(SingleLazySync.class){
                    if (mInstance == null){
                        mInstance = new SingleLazySync();
                    }
                }
            }
            return mInstance;
        }
    }

3.4 单例——静态内部类(常用)

    /**
     * Created by ych on 2019/7/20.
     * Description: 单例:静态内部类
     */
    public class SingleStaticInClass {
        private volatile static SingleStaticInClass mInstance;
    
        private SingleStaticInClass(){
    
        }
    
        private static SingleStaticInClass getInstance(){
            return SingleStaticInClassHolder.mInstance;
        }
    
        static class SingleStaticInClassHolder{
           private static SingleStaticInClass mInstance = new SingleStaticInClass();
        }
    
    }

3.5 单例——容器管理(系统服务使用的方式,等下看下源码)
  通过静态代码块进行初始化,代码块也是随着类的加载首先执行做一些进行初始化操作,使用集合进行存储

    /**
     * Created by ych on 2019/7/20.
     * Description: 单例 -----容器管理,系统服务使用就是这个
     */
    public class SingleSystemService {
    
        private static Map<String,Object> mSingleMap = new HashMap<>();
    
        static {
            mSingleMap.put("layout_inflater",new SingleSystemService());
        }
    
        private SingleSystemService(){
    
        }
    
        /**
         * 获取对象
         * @param systemName
         * @return
         */
        private static Object getSystemService(String systemName){
            return mSingleMap.get(systemName);
        }
    }

3.6 单例——容器管理(系统中使用LayoutInflater源码)
  通过源码可知,我们常用的LayoutInflate其实也是一个单例的

    /**
     * Obtains the LayoutInflater from the given context.
     */
     
    public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater";

    public static LayoutInflater from(Context context) {
        LayoutInflater LayoutInflater =
                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (LayoutInflater == null) {
            throw new AssertionError("LayoutInflater not found.");
        }
        return LayoutInflater;
    }

    /**
    * ContextImpl(ContextImpl是Context的实现类) 中的 getSystemService方法
    */
    @Override
    public Object getSystemService(String name) {
        return SystemServiceRegistry.getSystemService(this, name);
    }

    /**
     * 
     * 4.根据key 获取对应的服务  name 就是我们刚开始传入的 Context.LAYOUT_INFLATER_SERVICE = "layout_inflater"
     */
    public static Object getSystemService(ContextImpl ctx, String name) {
        ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
        return fetcher != null ? fetcher.getService(ctx) : null;
    }

    /**
    * 1.Service容器,是一个静态的Map集合,通过key  Context.LAYOUT_INFLATER_SERVICE = "layout_inflater"获取我们的LayoutInflate对象
    */
    private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
            new HashMap<String, ServiceFetcher<?>>();

    /**
    * 2. 注册服务器
    */
    private static <T> void registerService(String serviceName, Class<T> serviceClass,
            ServiceFetcher<T> serviceFetcher) {
        SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
        SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
    }
    
    /**
    *3. 静态代码块,第一次加载该类执行 (只执行一次,保证实例的唯一性)
    */
    static {
        //...省略系统其他部分
        //我们的LayoutInflate
        registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
                    new CachedServiceFetcher<LayoutInflater>() {
                @Override
                public LayoutInflater createService(ContextImpl ctx) {
                    return new PhoneLayoutInflater(ctx.getOuterContext());
                }});
        
        //常见的ActivityService
        registerService(Context.ACTIVITY_SERVICE, ActivityManager.class,
                    new CachedServiceFetcher<ActivityManager>() {
                @Override
                public ActivityManager createService(ContextImpl ctx) {
                    return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());
                }});
        ...     
    }

4.volatile关键字好处

4.1 防止重排序

  SingletonHungry mInstance = new SingletonHungry()
  我们创建一个对象都做了什么东西?

    1.开辟一块内存
    2.初始化对象
    3.给变量进行赋值(把这个变量指向内存地址)

  注意:如果是多线程的情况下,2和3的执行顺序是不固定的
     如果3先执行了,此时mInstance已经不为空了,我们对象还未初始化完成,我们使用该对象的时候可能会出现问题,所以我们可以使用volatile关键字

在这里插入图片描述
4.2 线程可见性

  当某一个线程修改了公用对象(变量),短时间内另一个线程可能是不可见的,因为每一个线程都有自己的缓存区(线程工作区)

 4.2.1简单的测试
 public class VolatileTest {

    public static void main(String[] args){
        //创建一个线程
        VolatileThread volatileThread = new VolatileThread();
        Thread thread = new Thread(volatileThread);
        thread.start();

        while (true){
            if (volatileThread.isFlag()){
                System.out.println("---------------");
                break;
            }
        }
    }

    private static class VolatileThread implements Runnable{
        private volatile boolean flag = false;

        @Override
        public void run() {
            try {
                Thread.sleep(200);
                flag = true;
                System.out.println("flag = " + isFlag());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        public boolean isFlag() {
            return flag;
        }

        public void setFlag(boolean flag) {
            this.flag = flag;
        }
    }
}

可能大家都会觉得会输出:
 flag = true
 哈哈哈哈哈哈

注意:1.当我不添加 volatile 关键时只会打印 flag = true
   2.当我添加 volatile 关键时才会打印 flag = true 哈哈哈哈哈哈

 4.2.2效果
在这里插入图片描述

5.总结

5.1 优点

  1.单例模式一个应用程序中一个类只有一个实例对象,减少我们内存的开销,特别是频繁创建和销毁时,此时就可以使用单例模式
  2.单例模式可以避免对资源的多重占用,比如一个写文件操作,由于只有一个实例存在内存中,避免对同一资源文件的同时写操作

5.2 缺点

  1.单例对象如果持有Context,那么很容易造成内存泄漏,所有我们传递给单例对象的Context最好是Application Context

6.UML图

image.png

相关文章

网友评论

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

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