简说单例

来源:互联网 发布:西餐厅收银软件 编辑:程序博客网 时间:2024/05/22 03:31

单例方式

看到很多人写单例都不考虑线程安全,当多线程操作当时候,就可能出现多个实例,那么不就违背了单例的初衷了么?如果要维护到单例类中到成员,那就更尴尬了,我看到也实在是无奈,所以这里建议大家写单例是需要做到线程安全!!

首先说推荐单例模式写法,主流用法。

  • DCL (Double CheckLock)
public class Singleton {    private Singleton() {    }    /**     * instance = new Singleton();因为不是一个原子操作,所以在编译到时候大致有三件事     * 1:给Singleton实例分配内存     * 2:调用好Sinleton() 构造函数,初始化成员字段     * 3:将instance对象指向分配到内存空间(执行了这一步后instance将为null了)     * 但是:     * 在JDK1.5之前有JMM(java Memory Model内存模型)中Cache、寄存器到驻内存回写顺序到规定,会导致执行顺序到不确定性     * 可能123 ,也可能132。     * 再1.5之后 JDK调整了JMM,加入了volatile关键字     *     * 这里加入volatile 关键字主要是为了保证每次都从驻内存获取     */    private volatile static Singleton instance;    public static Singleton getInstance() {        if (instance == null) {            synchronized (Singleton.class) {                if (instance == null)                    instance = new Singleton();            }        }        return instance;    }}

DCL写法优点就是在第一次执行到时候才会实例化,效率高。缺点就是第一次加载反应稍慢,性能牺牲点。再JDK1.6以后使用,这种方式是不错的选择。

  • 静态内部类写法:
private Singleton() {    }    private static class SingletonClassHolder {        private static final Singleton instance = new Singleton();    }    public static Singleton getInstance() {        return SingletonClassHolder.instance;    }

这是种单例方式,不光延迟了实例化,效率也高,也不需要双重检查锁定,并发上,性能就上去了。

  • 枚举
public enum Singleton {    INSTANCE;    public void doS() {        Log.e("11", "11");    }}

枚举最大的优点就是简单。枚举在java种与普通类式一样的,用这种方式来做单例也式很好的,最重要的是在任何情况下,它都式线程安全的,任何情况下它都只有一个实例。

其他单例在反序列化的时候会因为readResolve方法,而重新生成对象

//其他单例实现种,反序列化需要用这种方式来避免重新生成对象private Object readResolve() throws ObjectStreamException{    return instance;}

另外 :

  • 饿汉式:
private static final Singleton instance = new Singleton();    private Singleton() {    }    public static Singleton getInstance() {        return instance;    }

看了前面,在看这个就明显了,在声明的时候就会实例化,也不管需不需要使用。所以一般不推荐使用,可根据应用场景选择。

  • 懒汉式:
    private static Singleton instance;    private Singleton() {    }    public static synchronized Singleton getInstance() {        if (instance == null) {            instance = new Singleton();        }        return instance;    }

懒汉式式做到了再需要到时候才实例化了,但是每次获取的时候都需要同步,增加了开销,一般不推荐使用这种方式。

说说反射获取

介绍了5种单例方式,不线程安全的就不在这里说了。

除了枚举以外的方式,都可以使用反射来获取对象,采用如下方式即可:

Class<?> class1 = null;        try {            class1 = Class.forName("com.demo.Singleton");            Method m = class1.getMethod("getInstance");            Singleton single = (Singleton) m.invoke(null);            //获取到之后执行            single.doSomething();        } catch (ClassNotFoundException e) {            e.printStackTrace();        } catch (IllegalAccessException e) {            e.printStackTrace();        } catch (NoSuchMethodException e) {            e.printStackTrace();        } catch (InvocationTargetException e) {            e.printStackTrace();        }

而枚举就不可以咯,会报错的。

所以枚举方式确实很不错。只是在android种不太推荐枚举,官方这样说的:
image,会消耗更多的内存,影响性能问题。对手机内存不大的,是个灾难。
根据实际情况来选择对应的单例模式就可以了。

原创粉丝点击