设计模式 ~ 单例模式分析与实战

设计模式系列文章目录导读:

设计模式 ~ 面向对象 6 大设计原则剖析与实战
设计模式 ~ 模板方法模式分析与实战
设计模式 ~ 观察者模式分析与实战
设计模式 ~ 单例模式分析与实战
设计模式 ~ 深入理解建造者模式与实战
设计模式 ~ 工厂模式剖析与实战
设计模式 ~ 适配器模式分析与实战
设计模式 ~ 装饰模式探究
设计模式 ~ 深入理解代理模式
设计模式 ~ 小结

单例模式 (Singleton Pattern) 是一个非常简单的模式,也是开发者普遍用到的一个模式

单例模式定义:确保某一个类只有一个实例,而且自行实例化,并向这个系统提供这个实例

单例模式通用类图如下所示:

Singleton UML

单例模式的实现方式

单例模式的写法主要有

  • 饿汉模式
  • 懒汉模式
  • 双重检查锁模式
  • 静态内部类单例模式
  • 枚举类实现单例模式

饿汉模式

饿汉模式的单例很简单,定义静态变量的时候直接创建了单例对象,这样不会有什么线程安全问题,但是 JVM 在加载这个类的时候就会创建单例对象,如果该类还有其他静态成员,有的时候只会用到这个静态成员但是没用到该类的实例,会造成空间的浪费

public class Singleton1 {

    private static Singleton1 instance = new Singleton1();

    // 禁止外部实例化
    private Singleton1() {
    }

    public static Singleton1 getInstance() {
        return instance;
    }

}

懒汉模式

懒汉的模式将单例的创建延迟到了获取单例的方法里,等到用到的时候在创建单例对象,这样避免了空间的浪费

但是在高并发的情况下,这种方式的单例效率不高,synchronized 关键字是放在静态方法上,说明它的锁是 Singleton2.class,其他线程会等待里面的线程释放锁,所以这种方式在高并发的情况下性能不是很高:

public class Singleton2 {

    private static Singleton2 instance;

    private Singleton2() {
    }

    public static synchronized Singleton2 getInstance() {
        if (instance == null) {
            instance = new Singleton2();
        }
        return instance;
    }
}

双重检查锁模式

双重检查的锁的模式(Double-checked locking, 简称 DCL)是在懒汉模式的基础上改造而来,将同步方法改成同步代码块,因为大部分的情况下 instance 对象都不是为空的,所以如果不为空,直接返回即可,在高并发的情况下提高系统性能:

public class Singleton3 {

    private static volatile Singleton3 instance;

    private Singleton3() {
    }

    public static Singleton3 getInstance() {
        if (instance == null)
            synchronized (Singleton3.class) {
                if (instance == null) {
                    instance = new Singleton3();
                }
            }
        return instance;
    } 
}

读者可能会问为什么静态变量 instance 需要 volatile 修饰呢?

这主要是为了避免 空指针异常,问题的根源是 JVM 指令重排导致的:

public static Singleton3 getInstance() {      //1行
    if (instance == null)                     //2行
        synchronized (Singleton3.class) {     //3行
            if (instance == null) {           //4行
                instance = new Singleton3();  //5行
            }                                 //6行
        }                                     //7行
    return instance;                          //8行
}                                             //9行

问题出在第 5 行: instance = new Singleton3(),这一行代码可以分解为如下的三行伪代码:

memory = allocate();   //1:分配对象的内存空间 
ctorInstance(memory);  //2:初始化对象 
instance = memory;     //3:设置 instance 指向刚分配的内存地址 

指令重排可能将上面的3行伪代码重排为:

memory = allocate();   //1:分配对象的内存空间 
instance = memory;     //3:设置 instance 指向刚分配的内存地址 
                       // 注意,此时对象还没有被初始化!
ctorInstance(memory);  //2:初始化对象

指令重排后,在单线程的情况下不会有问题,因为访问对象的成员会在初始化对象后;但是在多线程的情况下可能会出现对象不为空,但是对象还没有初始化。所以变量加上 volatile 关键字能够禁止指令重排。

静态内部类单例模式

除了双重检查锁模式外,还可以通过静态内部类的方式实现单例模式的懒加载

这利用了 JVM 类加载机制,实现单例的懒加载。JVM 在加载类的的时候,如果没有用到它里面的静态内部类,是不是加载里面的静态内部类的,自然就不会初始化静态内部类的静态对象:

public class Singleton4 {

    private Singleton4(){
    }

    private static class InstanceHolder{
        private  final static Singleton4 instance = new Singleton4();

    }
    
    public static Singleton4 getInstance(){
        return InstanceHolder.instance;
    }
}

枚举实现单例模式

通过枚举类来实现单例也是 《Effective Java》所推荐的:

总而言之,你应该尽可能地使用枚举类型来实现实例控制的约束条件,如果做不到,同时有需要既可序列化又是实例控制的类,就必须提供一个 readResolver 方法,并确保该类的所有类型都为基本类型,或者是 transient 的

上面的实现的单例方式都是没有考虑序列化的情况,并没有提供 readResolver 方法

public enum Singleton5 {
    INSTANCE;
}

实践

在实际开发中,使用单例的情况还是非常多的,全局只需要存在一个实例的,都可以使用单例,比如 Android 中对 Activity 的管理类:Activity 创建的时候将 Activity 交给单例管理,销毁的时候将 Activity 从单例里的容器中移除,满足各种关闭页面的需求:

public class AppActivityManager {

	//private Stack<Activity> stack;
    private CopyOnWriteArrayList<Activity> stack;

    private static volatile AppActivityManager instance;

    private AppActivityManager() {
        stack = new CopyOnWriteArrayList<>();
    }

    public static AppActivityManager get() {
        if (instance == null)
            synchronized (AppActivityManager.class) {
                if (instance == null) {
                    instance = new AppActivityManager();
                }
            }
        return instance;
    }

    public void add(Activity activity) {
        stack.add(activity);
    }

    /**
     * 获取当前界面Activity(栈顶)
     *
     * @return Activity
     */
    public Activity getActivity() {
        if (stack.size() == 0) {
            return null;
        }
        //return stack.lastElement();
        return stack.get(stack.size() - 1);
    }

    /**
     * 关闭当前界面Activity(栈顶)
     */
    public void finishActivity() {
        if (stack.size() == 0) {
            return;
        }
        Activity activity = stack.get(stack.size() - 1);
        activity.finish();
    }

    /**
     * 关闭特定的Activity界面
     *
     * @param activity
     */
    public void finishActivity(Activity activity) {
        if (activity != null) {
            stack.remove(activity);
            activity.finish();
        }
    }

    public void finishActivity(Class clazz) {
        Iterator<Activity> it = stack.iterator();
        while (it.hasNext()) {
            Activity activity = it.next();
            if (null != activity && activity.getClass().equals(clazz)) {
                it.remove();
                activity.finish();
            }
        }
    }

    /**
     * 关闭的Activity界面在其他模块
     *
     * @param routerPath Activity path
     */
    public void finishActivity(String routerPath) {
        Class clazz = RouteManager.getInstance().getRoute(routerPath);
        if (clazz == null) {
            return;
        }
        Iterator<Activity> it = stack.iterator();
        while (it.hasNext()) {
            Activity activity = it.next();
            if (null != activity && activity.getClass().equals(clazz)) {
                it.remove();
                activity.finish();
            }
        }
    }

    private List<Class> getClassesInRouter(List list) {
        if (list == null || list.isEmpty()) {
            return null;
        }
        List<Class> classes = new ArrayList<>(list.size());
        for (Object obj : list) {
            if (obj instanceof Activity) {
                classes.add(obj.getClass());
            } else if (obj instanceof Class) {
                classes.add((Class) obj);
            } else if (obj instanceof String) {
                Class clazz = RouteManager.getInstance().getRoute(obj.toString());
                if (clazz != null) {
                    classes.add(clazz);
                }
            }
        }
        return classes;
    }


    /**
     * @param list 里面的元素可以是Activity、Activity的Class、Activity的routerPath
     */
    public void finishActivity(List list) {
        List<Class> classes = getClassesInRouter(list);
        if (classes == null || classes.isEmpty()) {
            return;
        }
        for (Class clazz : classes) {
            Iterator<Activity> it = stack.iterator();
            while (it.hasNext()) {
                Activity activity = it.next();
                if (null != activity && activity.getClass().equals(clazz)) {
                    it.remove();
                    activity.finish();
                }
            }
        }
    }


    /**
     * 关闭所有界面
     */
    public void finishAllActivity() {
        Iterator<Activity> it = stack.iterator();
        while (it.hasNext()) {
            Activity activity = it.next();
            if (null != activity) {
                it.remove();
                activity.finish();
            }
        }
    }

    public void finishAllActivityExcept(Class activityClass) {
        if (activityClass == null) {
            return;
        }
        Iterator<Activity> it = stack.iterator();
        while (it.hasNext()) {
            Activity activity = it.next();
            if (null != activity && !activity.getClass().equals(activityClass)) {
                it.remove();
                activity.finish();
            }
        }
    }

    /**
     * 关闭区间所有界面,包含begin和end。如栈中有A、B、C、D、E、F,想关闭C到F之间的Activity,begin参数就是C,end参数就是F
     *
     * @param begin
     * @param end
     */
    public void finishAllByRange(Class begin, Class end) {
        //判断start和end是否都在stack中
        Iterator<Activity> it = stack.iterator();
        Activity beginActivity = null, endActivity = null;
        while (it.hasNext()) {
            Activity activity = it.next();
            if (activity == null) {
                it.remove();
                continue;
            }
            if (activity.getClass().equals(begin)) {
                beginActivity = activity;
            } else if (activity.getClass().equals(end)) {
                endActivity = activity;
            }
        }

        boolean closable = false;
        if (beginActivity != null && endActivity != null) {
            Iterator<Activity> iterator = stack.iterator();
            while (iterator.hasNext()) {
                Activity activity = iterator.next();
                if (closable) {
                    activity.finish();
                    iterator.remove();
                    continue;
                }
                if (activity == beginActivity) {
                    closable = true;
                    beginActivity.finish();
                    iterator.remove();
                } else if (activity == endActivity) {
                    endActivity.finish();
                    iterator.remove();
                    break;
                }
            }
        } else if (beginActivity != null) {
            beginActivity.finish();
        } else if (endActivity != null) {
            endActivity.finish();
        }

    }

    /**
     * 关闭所有界面,然后跳转到某个界面,支持参数自动注入
     *
     * @param context
     * @param targetClass 需要跳转的目标界面
     * @param extras      传递参数给目标界面
     */
    public void finishAllStartTo(Context context, Class targetClass, Bundle extras) {
        finishAllActivity();
        if (targetClass == null) {
            return;
        }

        Intent intent = new Intent(context, targetClass);
        if (extras != null) {
            intent.putExtras(extras);
        }
        context.startActivity(intent);
    }


    /**
     * 获取栈中Activity数量
     */
    public int getActivityCount() {
        if (stack != null) {
            return stack.size();
        }
        return 0;
    }


}

Reference


如果你觉得本文帮助到你,给我个关注和赞呗!

另外,我为 Android 程序员编写了一份:超详细的 Android 程序员所需要的技术栈思维导图

如果有需要可以移步我的 GitHub -> AndroidAll,里面包含了最全的目录和对应知识点链接,帮你扫除 Android 知识点盲区。 由于篇幅原因只展示了 Android 思维导图:
超详细的Android技术栈

发布了161 篇原创文章 · 获赞 1125 · 访问量 125万+

猜你喜欢

转载自blog.csdn.net/johnny901114/article/details/100639132