设计模式系列文章目录导读:
设计模式 ~ 面向对象 6 大设计原则剖析与实战
设计模式 ~ 模板方法模式分析与实战
设计模式 ~ 观察者模式分析与实战
设计模式 ~ 单例模式分析与实战
设计模式 ~ 深入理解建造者模式与实战
设计模式 ~ 工厂模式剖析与实战
设计模式 ~ 适配器模式分析与实战
设计模式 ~ 装饰模式探究
设计模式 ~ 深入理解代理模式
设计模式 ~ 小结
单例模式 (Singleton Pattern) 是一个非常简单的模式,也是开发者普遍用到的一个模式
单例模式定义:确保某一个类只有一个实例,而且自行实例化,并向这个系统提供这个实例
单例模式通用类图如下所示:
单例模式的实现方式
单例模式的写法主要有
- 饿汉模式
- 懒汉模式
- 双重检查锁模式
- 静态内部类单例模式
- 枚举类实现单例模式
饿汉模式
饿汉模式的单例很简单,定义静态变量的时候直接创建了单例对象,这样不会有什么线程安全问题,但是 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
- 《设计模式之禅》
- 《Java设计模式及实践》
- 《Java设计模式深入研究》
- 《设计模式(Java版)》
- 《深入理解Java虚拟机》
- 《Effective Java》
- 《Android源码设计模式解析与实战》
- https://juejin.im/post/5d64ca62f265da03b638bb47
- double-checked-locking-with-delay-initialization
下面是我的公众号,干货文章不错过,有需要的可以关注下,有任何问题可以联系我:
转载:https://blog.csdn.net/johnny901114/article/details/100639132