美文网首页
单例模式几种实现方式

单例模式几种实现方式

作者: 今年五年级 | 来源:发表于2021-10-27 17:43 被阅读0次

    特点:

    1. 单例类只能有一个实例
    2. 单例类必须自己创建自己的唯一实例
    3. 单例类必须给所有其他对象提供这一实例

    1. 饿汉式

    上来就创建对象

    2. 懒汉式

    需要时才创建


    此方式单线程下无问题,多线程下出问题,测试代码如下:

    多尝试几次出现错误情况

    多线程下懒汉式错误时序

    3. 懒汉式多线程下改进版1之(将获取单例方法改为同步方法)

    这下多线程下安全问题解决了,但是效率低下,整个方法都加锁。
    正常情况下,一堆线程过来,起码会有一些线程会判断成功直接返回,直接方法加synchronized锁,除了一个线程,其他线程都要排队等待走一遍,同步方法对性能影响非常大,因此有下面的改进

    4. 懒汉式多线程下改进版2之DCL(double checked locking)双重校验锁

    注意上面单例对象前面的volatile关键字必须加,原因:
    下面的第二行代码,在创建对象的时候,分为3个步骤

    • 分配内存空间
    • 初始化对象
    • 将对象指向刚分配的内存空间

    但是有些编译器为了性能的原因,可能会将第二步和第三步进行重排序,顺序就成了:

    • 分配内存空间
    • 将对象指向刚分配的内存空间
    • 初始化对象

    现在考虑重排序后,两个线程发生了以下调用:在这种情况下,T7时刻线程B对uniqueSingleton的访问,访问的是一个初始化未完成的对象。

    对象完整创建过程如下:

    HotSpot虚拟机创建对象过程

    5. 内部静态辅助类方式

    当单例类被加载的时候,内部辅助类还没有加载到内存,当执行静态获取单例对象方法时内部类才被加载,然后创建单例对象
    即私有内部静态类负责创建单例类的实例对象,且不需要同步机制

    6. 枚举方式

    注意
    对于上面的饿汉式,懒汉式之类的我们可以通过反射暴力破解私有构造方法其实是可以破坏单例的,但是枚举方式是无法破坏的,我们使用如下代码示例

     public static void main(String[] args) throws Exception {
            SingleTon singleton1 = SingleTonEnum.INSTACE.getSingleTon();
            SingleTon singleton2 = SingleTonEnum.INSTACE.getSingleTon();
            System.out.println("正常情况下,实例化两个实例是否相同:" + (singleton1 == singleton2));
            Constructor<SingleTonEnum> constructor= SingleTonEnum.class.getDeclaredConstructor();
            constructor.setAccessible(true);
            SingleTon singleton3;
            singleton3 = constructor.newInstance().getSingleTon();
            System.out.println(singleton1 + "\n" + singleton2 + "\n" + singleton3);
            System.out.println("通过反射攻击单例模式情况下,实例化两个实例是否相同:" + (singleton1 == singleton3));
        }
    

    执行结果:

    出现这个异常的原因是因为SingleTonEnum.class.getDeclaredConstructors()获取所有构造器,会发现并没有我们所设置的无参构造器,而且在反射在通过newInstance创建对象时,会检查该类是否ENUM修饰,如果是则抛出异常,反射失败。所以枚举是不怕发射攻击的

    相关文章

      网友评论

          本文标题:单例模式几种实现方式

          本文链接:https://www.haomeiwen.com/subject/mmjpaltx.html