单例(Singleton)模式
定义
单例模式是一种对象创建型模式,使用单例模式,可以保证为一个类只生成唯一的实例对象。也就是说,在整个程序空间中,该类只存在一个实例对象。
GoF对单例模式的定义是:保证一个类,只有一个实例存在,同时提供该实例加以访问的全局访问方法。
使用场景
确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多资源,或者某种类型的对象只应该有且只有一个。
uml图
角色介绍:
- Client--高层客户端
- Singleton--单例类
实现单例主要有以下几个关键点
- 构造函数不对外开放,一般为private
- 通过一个静态方法或者枚举返回单例类对象
- 确保单例类的对象有且只有一个,尤其是在多线程环境下
- 确保单例类对象在反序列时不会重新构建对象。
单例模式的各种写法
饿汉式
代码:
class HungrySingleton{
private static HungrySingleton singleton=new HungrySingleton();
//构造函数私有
private HungrySingleton(){}
//静态方法获取实例
public static HungrySingleton getInstance(){
return singleton;
}
}
优点:饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。
缺点:资源利用效率不高,可能getInstance永远不会执行到,但是执行了该类的其他静态方法或者加载了该类(class.forName),那么这个实例仍然初始化了
懒汉式
代码:
class LazySingleton{
private static LazySingleton singleton;
//构造方法私有
private LazySingleton(){
}
//synchrocized 保证了同步
public static synchronized LazySingleton getInstance(){
if(singleton==null){
singleton=new LazySingleton();
}
return singleton;
}
}
优点:单例只有在使用时才会被实例化,在一定程度上节约了资源。
缺点:synchrocized是getInstance方法在多线程情况下保证单例对象唯一性手段,细想一下,即使singleton在第一次的时候已经被初始化了,每次调用getInstance方法都会进行同步,这样就会消耗不必要的资源,这也是懒汉式存在的最大问题。所以一般情况下不建议使用这种情况。
双重检查锁定(Double Check Lock(DCL))
代码:
public class Singleton {
//使用volatile关键字
private static volatile Singleton sInstance=null;
private Singleton() {
}
public static Singleton getInstance() {
if (sInstance == null) {
synchronized (Singleton.class) {
if (sInstance == null) {
sInstance = new Singleton();
}
}
}
return sInstance;
}
}
在getInstance方法中对instance进行了两次判空:第一层判断是为了避免不必要的同步,第二层的判断则是为了在null的情况下创建实例。
下面详细说明这个问题:
假如线程 A 执行到了sInstance = new Singleton();
语句,这看起来是一句代码,但在句代码大致做了三件事:
- 给 Singleton 分配内存,
- 调用 Singleton 的构造函数,初始化成员字段。
- 将 sInstance 对象指向分配的内存空间(这个时候 sInstance 就不是 null 了)
由于java编译器是允许处理器乱序执行,上面的第二,第三的顺序是无法保证的,就是说上面的执行顺序可能是 1,2,3 也可能是1,3,2。这样就有问题了,如果线程A中执行的顺序是1,3,2,那么在3执行完毕,2 未执行之前(这个时候 sInstance 就不是 null),被切换到线程B上,由于sInstance不为null,所以线程B直接获取了sInstance,由于这个sInstance没有执行2,所以使用时会出现错误。
上面就是DCL失效问题。
在jdk1.5 以后,SUN注意到这个问题,调整了jvm,具体化了 volidate 关键字,所以在jdk1.5以后使用volidate就可以保证sInstance 对象每次都是从主内存中读取,就可以使用DCL的写法来完成单例模式。
优点:资源利用率高,第一次执行getInstance时才会被实例化,效率快。
缺点:第一次加载时反应有点慢。
静态内部类
代码:
public class Singleton {
//构造函数私有
private Singleton() {
}
//获取实例
private static Singleton getInstance() {
return SingletonHolder.sInstance;
}
/**
* 静态内部类 */
private static class SingletonHolder {
private static final Singleton sInstance = new Singleton();
}
}
这种方式不仅保证了线程安全,也保证单例对象的唯一性,同时也延迟了单例的实例化,所以推荐使用这种方法来打造单例模式。
枚举
代码:
public enum Singleton {
INSTANCE
}
枚举写法简单,枚举实例的创建时线程安全的,并且在任何情况下它都是一个单例。
在Android中运用
我们在应用中经常需要关闭页面的操作,如果直接使用finish(),有很多效果达不到,这就需要我们对Activity进行统一管理
Android系统有自己的Activity管理机制,也就是 Activity Stack(栈)。奉行着 先进后出,后进先出的原则。那么我们就通过Stack来进行Activity的管理。
实现包括:添加Activity到堆栈、获取当前的Activity(堆栈最后一个)、结束当前的Activity(堆栈最后一个)、结束指定的Activity、结束指定类名的Activity、结束所有的Activity等方法。
ActivityManager管理类
/**
* Activity管理类
*/
public class AppManager {
private static Stack<Activity> activityStack;
private static AppManager instance;
private AppManager() {
}
/**
* 单一实例
*/
public static AppManager getAppManager() {
if (instance == null) {
instance = new AppManager();
}
return instance;
}
/**
* 添加Activity到堆栈
*/
public void addActivity(Activity activity) {
if (activityStack == null) {
activityStack = new Stack<Activity>();
}
activityStack.add(activity);
}
/**
* 获取当前Activity(堆栈中最后一个压入的)
*/
public Activity currentActivity() {
Activity activity = activityStack.lastElement();
return activity;
}
/**
* 结束当前Activity(堆栈中最后一个压入的)
*/
public void finishActivity() {
Activity activity = activityStack.lastElement();
finishActivity(activity);
}
/**
* 结束指定的Activity
*/
public void finishActivity(Activity activity) {
if (activity != null) {
activityStack.remove(activity);
activity.finish();
activity = null;
}
}
/**
* 结束指定类名的Activity
*/
public void finishActivity(Class<?> cls) {
for (Activity activity : activityStack) {
if (activity.getClass().equals(cls)) {
finishActivity(activity);
}
}
}
/**
* 结束所有Activity
*/
public void finishAllActivity() {
for (int i = 0, size = activityStack.size(); i < size; i++) {
if (null != activityStack.get(i)) {
activityStack.get(i).finish();
}
}
activityStack.clear();
}
/**
* 退出应用程序
*/
@SuppressWarnings("deprecation")
public void AppExit(Context context) {
try {
finishAllActivity();
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
activityManager.restartPackage(context.getPackageName());
System.exit(0);
} catch (Exception e) {
e.printStackTrace();
}
}
}
以上代码来自Android源码设计模式和android Activity管理类(全局管理Activity)