设计模式:单例模式

一、定义

保证这个程序中只有一个实例

二、基本写法

  1. 构造函数私有,防止外部new对象
  2. 内部提供一个静态方法,让外部调用

饿汉式

//单例模式 -- 饿汉式
public class Singleton {
    //随着对象的创建就去new 
    private static Singleton mInstance = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance(){
        return mInstance;
    }
}

这种方式在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快。 这种方式基于类加载机制避免了多线程的同步问题

懒汉式

初始样式(线程不安全)
//单例模式 -- 懒汉式
public class Singleton {
    //只有使用的时候才去new对象,更加高效,
    private static Singleton mInstance ;

    private Singleton() {
    }

    
    public static Singleton getInstance(){
        if (mInstance == null){
            //会出现多线程并发问题
            mInstance = new Singleton();
        }
        return mInstance;
    }
}

懒汉模式申明了一个静态对象,在用户第一次调用时初始化,虽然节约了资源,但第一次加载时需要实例化,反应稍慢一些,而且多线程中会有线程安全问题。

解决懒汉式在多线程下的并发问题(线程安全)
//单例模式 -- 懒汉式
public class Singleton {
    private static Singleton mInstance;

    private Singleton() {
    }

    //解决懒汉式多线程并发问题,但是会出现效率低的问题,每次获取都要经过同步锁的判断
    public static synchronized Singleton getInstance(){
        if (mInstance == null){
            mInstance = new Singleton();
        }
        return mInstance;
    }   
}

这种写法能够在多线程中很好的工作,但是每次调用getInstance方法时都需要进行同步,造成不必要的同步开销,而且大部分时候我们是用不到同步的,所以不建议用这种模式。

解决每次获取都要经过同步锁判断导致的线程低的问题  -- 双重检验(DCL
//单例模式 -- 懒汉式
public class Singleton {
    private static Singleton mInstance;

    private Singleton() {
    }

   //既保证了线程安全,效率也是比较高,但是会出现指令重排序问题
    public static Singleton getInstance() {
        if (mInstance == null) {
            synchronized (Singleton.class) {
                if (mInstance == null) {
                    mInstance = new Singleton();
                }
            }
        }
        return mInstance;
    }
}

解决指令重排序问题 -- 加volatile关键字

//单例模式 
public class Singleton {
    private static volatile Singleton mInstance;

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

这种写法在getSingleton方法中对singleton进行了两次判空,第一次是为了不必要的同步,第二次是在singleton等于null的情况下才创建实例。

volatile关键字有一点点影响性能,但考虑到程序的正确性,牺牲这点性能还是值得的。 DCL优点是资源利用率高,第一次执行getInstance时单例对象才被实例化,效率高。缺点是第一次加载时反应稍慢一些,在高并发环境下也有一定的缺陷,虽然发生的概率很小。DCL虽然在一定程度解决了资源的消耗和多余的同步,线程安全等问题,但是他还是在某些情况会出现失效的问题,也就是DCL失效

建议用静态内部类单例模式来替代DCL

静态内部类单例模式

public class Singleton { 
    private Singleton(){
    }
    public static Singleton getInstance(){  
        return SingletonHolder.sInstance;  
    }  
    private static class SingletonHolder {  
        private static final Singleton sInstance = new Singleton();  
    }  
} 

第一次加载Singleton类时并不会初始化sInstance,只有第一次调用getInstance方法时虚拟机加载SingletonHolder 并初始化sInstance ,这样不仅能确保线程安全也能保证Singleton类的唯一性,所以推荐使用静态内部类单例模式

枚举单例

public enum Singleton {  
     INSTANCE;  
     public void doSomeThing() {  
     }  
 }  

枚举单例的优点就是简单,但是大部分应用开发很少用枚举,可读性并不是很高,不建议用

使用容器实现单例模式

public class SingletonManager { 
  private static Map<String, Object> objMap = new HashMap<String,Object>();
  private Singleton() { 
  }
  public static void registerService(String key, Object instance) {
    if (!objMap.containsKey(key) ) {
      objMap.put(key, instance) ;
    }
  }
  public static ObjectgetService(String key) {
    return objMap.get(key) ;
  }
}

用SingletonManager 将多种的单例类统一管理,在使用时根据key获取对象对应类型的对象。这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。

总结:

        选择哪种单例模式,取决于项目本身,是否是有复杂的并发环境,是否需要控制单例对象的资源消耗。

指令重排序问题分析:

Singleton mInstance = new Singleton();

在创建一个对象时,会经历以下几个步骤:

  1. 开辟一块新的内存空间
  2. 初始化对象
  3. 给变量赋值,也就是指向内存地址

在Java对线程中,2和3的执行的顺序是不固定的,也就是Java的指令重排序

当以上步骤3先于步骤2执行时,就会先将变量赋值,也就是新开辟的内存地址,这时调用者去拿mInstance时是不为空的,但是又没赋值,所以会出现问题

每个线程都有自己的线程工作区,一个线程修改了公用对象,可能对其他线程是不可见的,修改只适用于自己的线程工作区

解决办法:加上volatile关键字,禁止重排序,保证可见性

三、开发中用到的单例

Activity管理类

public class ActivityManager {
    private static volatile ActivityManager mInstance;
    private Stack<Activity> mActivities;

    private ActivityManager(){
        mActivities = new Stack<>();
    }

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

    /**
     * 添加统一管理
     * @param activity
     */
    public void attach(Activity activity){
        mActivities.add(activity);
    }

    /**
     * 移除解绑 - 防止内存泄漏
     * @param detachActivity
     */
    public void detach(Activity detachActivity){
        int size = mActivities.size();
        for (int i = 0; i < size; i++) {
            Activity activity = mActivities.get(i);
            if (activity == detachActivity) {
                mActivities.remove(i);
                i--;
                size--;
            }
        }
    }

    /**
     * 关闭当前的 Activity
     * @param finishActivity
     */
    public void finish(Activity finishActivity){
        int size = mActivities.size();
        for (int i = 0; i < size; i++) {
            Activity activity = mActivities.get(i);
            if (activity == finishActivity) {
                mActivities.remove(i);
                activity.finish();
                i--;
                size--;
            }
        }
    }

    /**
     * 根据Activity的类名关闭 Activity
     */
    public void finish(Class<? extends Activity> activityClass){
        int size = mActivities.size();
        for (int i = 0; i < size; i++) {
            Activity activity = mActivities.get(i);
            if (activity.getClass().getCanonicalName().equals(activityClass.getCanonicalName())) {
                mActivities.remove(i);
                activity.finish();
                i--;
                size--;
            }
        }
    }

    /**
     * 退出整个应用
     */
    public void exitApplication(){

    }

    /**
     * 获取当前的Activity
     * @return
     */
    public Activity currentActivity(){
        return mActivities.lastElement();
    }
}

四、Android源码中的单例

ActivityManager

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值