Android设计模式(1)——7种单例模式的使用

一:单例模式介绍:

单例模式是模式应用最广的模式之一,也可能是很多初级工程师唯一会使用的设计模式。我们在应用单例模式的时候,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要一个全局的对象,例如在Android项目中:
一:我们假设有一个用户管理类,里面主要是用来处理当前是否是登录状态的的一些信息。因为在很多地方都需要我们去判断当前用户是否是登录状态。这里我们就可以吧这个用户管理类写成一个单例,保证该对象只会new一次。
二:应用只有一个ImageLoader实例,这个ImageLoader中又包含线程池,网络请求等等,很消耗资源。因为没必要每次使用都全构造多个实例,所以用一个单例模式再好不过了。

二:单例模式的几个关键点:

  1. 构造函数不对外开放,一般为Private。
  2. 通过一个静态方法或者枚举返回单例类对象。
  3. 确保单例类的对象有且只有一个,尤其是在多线程环境下。
  4. 单例类对象在反序列化时不会重新构建对象。**

三:单例模式的7种使用

1.懒汉模式–

懒汉模式是声明一个静态对象,并且在用户第一次调用getInstance的时候进行初始化,而且上述的在声明静态对象的时候就已经初始化。我们会发现第一次调用时就会被初始化ins,每次调用getInstance方法都会进行同步,这样会消耗不必要的资源。
缺点:第一次加载时需要及时的进行实例化,反应稍慢,最大问题每次调用getInstance都会进行同步,造成没必要的开销。不建议使用。

 public class Singleton{    
    private static Singleton ins;   
    private Singleton(){} 
    public static synchroized Singleton getInstance(){
    if(ins==null){
        ins=new Singleton();
    }
    return ins;
    }
}

2.懒汉模式–

缺点:在多线程中不能正常使用

public class Singleton{     
    private static Singleton ins;   
    private Singleton(){} 
    //只有static修饰
    public static Singleton getInstance(){
    if(ins==null){
        ins=new Singleton();
    }
    return ins;
    }
}

3.DCL方式实现单例(双重检查锁定)

我们可以看到getInstance方法中对sin进行了两次判空:第一层判断主要是为了避免不必要的同步,第二层的判断是为了在null的情况下创建实例。这里主要是在JDK1.5之后才能正常使用。
假设线程A执行到 sin=new Singleton(),最终编译的时候大致做了3件事情:

1.给Singleton实例分配内存
2.调用SIngleton构造函数,初始化。
3.将sin对象指向分配的内存空间(此时sin就不是null了)

由于Java编译器永续处理器乱序执行,在JDK1.5之前JMM中的Cache,寄存器到主内存回写顺序的规定,上面的第二和第三的顺序是无法保证的。执行顺序有可能是1-2-3,有可能是1-3-2。所以会导致很多问题,有兴趣的朋友可以查看关于这块的问题。
JDK1.5之后官方主要到这种问题调整了JMM,具体优化了volatile关键字,所以像下面这样修饰sin可以保证sin对象每次都是从主内存中读取,就可以使用DCL的写法来完成单例.
优点:资源利用率高,第一次执行getInstance时单例才会被实例化,效率高.
缺点:第一次加载反应稍慢,也由于java内存模型的原因偶尔会失败,在高并发环境下也有一定的缺陷,虽然发生概率很小。除非在JDK低于1.6版本下使用,否则这种方式一般能满足需求。

public class Singleton{
    private volatile static Singliton sin=null;
    private Singleton(){}
    public static Singleton getInstance(){
        if(sin==null){
            synchroized (Singleton.class){
                if(sin==null){
                    sin=new Singleton();
                }
            }
        }
        return sin;
    }
}

4.饿汉模式
饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的.习惯使用这种。

public class Singleton{
    private static Singleton sin=new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){
        return sin;
    }
}

5.枚举单例
写法简单是枚举单例的最大优点,枚举在java中与普通类是一样的,不仅能够有字段,还能够有自己的方法。最重要的使默认枚举实例的创建时线程安全的,并且在任何情况下它都是一个单例。反序列化可以通过特殊的途径去创建类的一个新的实例,相当于调用该类的构造函数。而枚举不会存在这个问题,因为即使反序列化它也不回重新生成新的实例。只不过通常我们很少用。

public enum SingletonEnum{
    INSTANCE; 
    public void doSomething(){
        System.out.println("");
    }
}

6.静态内部类单例模式
当第一加载Singleton类并不会初始化sin,只有在第一次调用getInstance方法时候才会导致sin被初始化,因此第一次调用getInstance方法会导致虚拟机加载SingLetonHo类,这种方式不仅能确保线程安全,也能够保证单例对象的唯一性,同时也延迟了单例的实例化。所以这是推荐使用的单例模式实现方式。

public class Singleton{
    private Singleton(){}
    public static Singleton getInstance(){
        return SingletonHo.sin;
    }
    //静态内部类
    private static class SingletonHo(){
        private static final Singleton sin=new Singleton();
    }
}

7.使用容器实现单例模式
将多种单例类型注入到一个统一的管理类中,在使用时根据key获取对象对应类型的对象。这种方式使我们可以管理多种类型的单例。降低了用户的使用成本,也降低了耦合度。

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

四:总结

不管以哪种形式实现单例模式,他们的核心原理都是将构造函数私有化,并且通过静态方法获取一个唯一的实例,在这个获取的过程中必须保证线程安全,防止反序列化导致重新生成实例对象等问题。选择哪种要看项目的本生,现在一般JDK’的版本都不会太低,所以很多单例模式都可以使用,具体看自己了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值