简述-单例模式

单例方式

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

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

  • 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,会消耗更多的内存,影响性能问题。对手机内存不大的,是个灾难。
根据实际情况来选择对应的单例模式就可以了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值