Android设计模式---单例

单例模式用来创建独一无二的对象,供全局使用,其特点如下:

  • 单例确保程序中一个类中只有一个实例
  • 提供全局访问点
  • 需要私有构造器,一个静态方法,一个静态变量
  • 确定在性能与资源上限制,适当的选择单例方案已解决多线程问题
  • 注意java版本(<2 会导致双重检查加锁失效,<1.2 会被GC掉),当然目前都是>5了
  • 如果使用多个类加载器(loader)会导致单例失效,也是会产生多个类实例的

    • 饿汉模式
    • 懒汉模式
    • 方法锁模式
    • 双重检查锁DCL
    • 双重检查加锁volatile
    • 静态内部类
    • 枚举类
    • 总结

饿汉模式

public class Singleton {

    private static Singleton singleton = new Singleton();

    private Singleton() {
    }

    public static Singleton getSingleton() {
        return singleton;
    }

    public void test() {
        // TODO: 2016/1/27
    }
}

优点

线程安全,一定是单例

缺点

事先加载好了,如果不使用造成内存浪费
不能防止反序列化、反射产生新的实例

比较常用的一种。

懒汉模式

public class Singleton {

    private static Singleton singleton = null;
    private Singleton(){
    }

    public static Singleton getSingleton() {
        if(null == singleton){
            singleton = new Singleton();
        }
        return singleton;
    }

    public void test(){
        // TODO: 2016/1/27
    }
}

优点

饿汉节省内存,伪单例

缺点

线程不安全
不能防止反序列化、反射产生新的实例

不推荐使用的!!!

方法锁模式

public class Singleton {

    private static Singleton singleton = null;
    private Singleton(){
    }

    public synchronized static Singleton getSingleton() {
        if(null == singleton){
            singleton = new Singleton();
        }
        return singleton;
    }

    public void test(){
        // TODO: 2016/1/27
    }
}

优点

线程安全,一定是单例

缺点

实例创建方法所影响性能
不能防止反序列化、反射产生新的实例

万不得已不要使用啊!!!效率很低

双重检查锁DCL

public class Singleton {

    private static Singleton singleton = null;
    private Singleton(){
    }

    public static Singleton getSingleton() {
        if(null == singleton){//①
            synchronized (Singleton.class){
                if(null == singleton){//②
                    singleton = new Singleton();//③
                }
            }
        }
        return singleton;
    }

    public void test(){
        // TODO: 2016/1/27
    }
}

优点

大部分时候线程安全,相比方法锁程序并发性较高

确定

线程并非绝对安全,有可能访问到单例的成员变量不同的值
不能防止反序列化、反射产生新的实例

线程不安全原因

Java的乱序执行、初始化对象需要时间。
对于new的时候,JVM在执行时大致做了下述三件事:
a. 在内存中分配一块内存
b. 调用构造方法
c. 将内存的地址指向instance变量(执行这一步后,instance != null)
如果按照abc的顺序执行也不会有什么问题。但由于Java乱序执行的机制,有可能在真实情况下执行顺序为acb。
假设t1、t2是两个线程。t1执行到①时,发现为null,于是执行到语句③,先执行a,再执行c,在还没有执行b时,时间片给了t2。这时,由于instance已经分配了地址空间,instance不为null了。所以t2在执行到语句①后直接return instance,获得了这个还没有被初始化的对象,然后在使用时就报错了。

双重检查加锁+volatile

public class Singleton {

    private static volatile Singleton singleton = null;
    private Singleton(){
    }

    public static Singleton getSingleton() {
        if(null == singleton){//①
            synchronized (Singleton.class){
                if(null == singleton){//②
                    singleton = new Singleton();//③
                }
            }
        }
        return singleton;
    }

    public void test(){
        // TODO: 2016/1/27
    }
}

优点

线程安全、一定是单例
线程安全的原因

使用了volatile关键字,volatile能都屏蔽指令重排序,使得③按照正常创建顺序执行。

缺点

不能防止反序列化、反射产生新的实例

静态内部类

public class Singleton {

    private static class SingletonHolder{
        static final Singleton singleton= new Singleton();
    }

    private Singleton() {
    }

    public static Singleton getSingleton() {
        return SingletonHolder.singleton;
    }
}

优点

线程安全,绝对单例
避免了在没有使用它的时候就已经加载实例。

线程安全的原因:Java的运行机制保证了static修饰的类、对象仅被初始化一次。

classloder的机制来保证初始化singleton时只有一个线程,Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getSingleton方法时,才会显示装载SingletonHolder类,从而实例化singleton。

缺点

不能防止反序列化、反射产生新的实例

枚举类

public enum Singleton {
    instance {
        String name = "this is singleton";

        public void setName(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }
    };

    public abstract void setName(String name);
    public abstract String getName();
}

优点

线程安全、绝对单例、可以防止因为反序列化、反射产生新的实例

缺点

和饿汉模式类似,会造成内存浪费。

总结

一般情况下使用饿汉模式和静态内部类比较多,并且都是线程安全的;如果涉及到反序列化创建对象时我会试着使用枚举的方式来实现单例;不建议使用懒汉/方法锁模式。如果有特殊的需求,我可能会使用双重检查加锁+volatile

你可能感兴趣的:(设计模式,Android,设计模式,java,android,单例)