首页 > 其他分享 >【博学谷学习记录】超强总结,用心分享 | 单例设计模式总结

【博学谷学习记录】超强总结,用心分享 | 单例设计模式总结

时间:2022-11-18 17:35:18浏览次数:54  
标签:总结 private INSTANCE static 单例 Singleton1 设计模式 public

单例设计模式

单例模式(Singleton Pattern)涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。


饿汉式

饿汉式就是在类加载的时候就进行初始化,基于classloader机制避免了多线程的同步问题。

这种方式比较常用,但容易产生垃圾对象,浪费内存。

public class Singleton1 { 
    private static final Singleton1 instance = new Singleton1(); 
    
    private Singleton1 (){} 
    
    public static Singleton1 getInstance() {  
    	return instance;  
    }  
}

但是,该单例代码能受到反射的破坏;如果该类实现了Serializable接口,还可以通过反序列化重写readResolve()方法来破环单例。

public class Singleton1 implements Serializable {
    private Singleton1() {
        if (INSTANCE != null) {
            throw new RuntimeException("单例对象不能重复创建");
        }
        System.out.println("private Singleton1()");
    }

    private static final Singleton1 INSTANCE = new Singleton1();

    public static Singleton1 getInstance() {
        return INSTANCE;
    }

    public Object readResolve() {
        return INSTANCE;
    }
}
  • 构造方法抛出异常是防止反射破坏单例
  • readResolve() 是防止反序列化破坏单例

但是无法防止 unsafe 破环单例


枚举饿汉式

这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。

public enum Singleton2 {
    INSTANCE;

    public static Singleton2 getInstance() {
        return INSTANCE;
    }

}

枚举饿汉式能天然防止反射、反序列化破坏单例


懒汉式

懒汉式单例的主要特点就是被外部类调用的时候内才会创建实例。

1、不加锁(非线程安全)

public class Singleton3 {
    private Singleton3() {}

    private static Singleton3 INSTANCE = null;

    public static Singleton3 getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new Singleton3();
        }
        return INSTANCE;
    }
}

2、方法加锁(线程安全)

public class Singleton3 {
    private Singleton3() {}

    private static Singleton3 INSTANCE = null;

    public static synchronized Singleton3 getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new Singleton3();
        }
        return INSTANCE;
    }

}

实际上,只有第一次创建单例对象的时候需要加同步锁,后续掉用反而会因为同步锁而阻塞,导致程序性能下降。

3、双重校验锁(DCL)

这种方式采用双锁机制,安全且在多线程情况下能保持高性能。

public class Singleton4 {
    private Singleton4() {}

    private static volatile Singleton4 INSTANCE = null; // 可见性,有序性

    public static Singleton4 getInstance() {
        if (INSTANCE == null) {
            synchronized (Singleton4.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton4();
                }
            }
        }
        return INSTANCE;
    }
}

1、为何必须加 volatile:

  • INSTANCE = new Singleton4() 不是原子的,分成 3 步:创建对象、调用构造、给静态变量赋值,其中后两步可能被指令重排序优化,变成先赋值、再调用构造
  • 如果线程1 先执行了赋值,线程2 执行到第一个 INSTANCE == null 时发现 INSTANCE 已经不为 null,此时就会返回一个未完全构造的对象

4、静态内部类

这种方式能达到双检锁方式一样的功效,但实现更简单。

对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。

这种方式只适用于 静态域 的情况,双检锁方式可在 实例域需要延迟初始化时 使用。

public class Singleton5 {
    private Singleton5() {}

    private static class Holder {
        static Singleton5 INSTANCE = new Singleton5();
    }

    public static Singleton5 getInstance() {
        return Holder.INSTANCE;
    }
}

内部类是在方法调用之前初始化的,巧妙的避免了线程安全问题。

静态内部类的写法既没有饿汉式的内存浪费问题,也没有加锁带来的性能开销,避免了双检锁的缺点。


JDK 中单例的体现

  • Runtime 体现了饿汉式单例模式

  • System中的Console 体现了双检锁懒汉式单例

  • Collections 中的 EmptyNavigableSet 内部类懒汉式单例
  • ReverseComparator.REVERSE_ORDER 内部类懒汉式单例
  • Comparators.NaturalOrderComparator.INSTANCE 枚举饿汉式单例

标签:总结,private,INSTANCE,static,单例,Singleton1,设计模式,public
From: https://www.cnblogs.com/Azureblue/p/16903983.html

相关文章

  • 在业务逻辑中,经常会有父组件调用子组件方法的情况,vue2.0 和 vue3.0 的使用中有些不一
    在业务逻辑中,经常会有父组件调用子组件方法的情况,vue2.0和vue3.0的使用中有些不一样,在这里总结下。vue2.0中的使用方法父组件:<template><div@click="fatherMeth......
  • iOS面试题总结
    iOS已经到了小公司用不起,大公司不招的地步了。当然,也没有实习生要来学这个。整个移动端都太难了,今年大家都太难了。面试了一些公司,就自我总结一下吧。有空也能背一下。基......
  • 信息论专题总结——特定信源分布的率失真函数计算
    之前在学信息论这门课的时候,对于求率失真的方法虽然记住了,但是并没有理解。这次重新梳理率失真的计算方法,并对重点问题的思考做一个记录。本轮梳理将以Cover&Thomas的EoI......
  • 微信小程序中textarea与input的问题总结
    textarea与input的相爱相杀测试机器本次探索所用到的机器为安卓机器:荣耀20,小米10s;ios机器:iPhone131、绑定bindkeyboardheightchange事件问题:只为textarea绑......
  • Java进阶篇——设计模式
    设计模式一、代理模式使用代理类对真实对象进行代理,包括真实对象方法的调用、功能的扩展等。访问的时候也只能访问到代理对象,既保护了真实对象同时可以在原始对象上进行......
  • 在微信小程序上做一个「博客园年度总结」:使用redis存储数据
    前面写过一篇博客:在微信小程序上做一个「博客园年度总结」:解决前端获取接口数据太慢的一种思路当时是从博客园接口获取数据比较慢,所以从博客园拿到数据后,先把数据存到一个......
  • SQL注入绕waf思路总结
    1.关键字大小写混合绕过关键字大小写混合只针对于小写或大写的关键字匹配技术-正则表达式,如果在匹配时大小写不敏感的话,就无法绕过。这是最简单的一个绕过技术。例如:将un......
  • Java内存马的学习总结
    1.前置知识JavaWeb三大组件ServletServlet是运行在Web服务器或应用服务器上的程序,它是作为来自HTTP客户端的请求和HTTP服务器上的数据库或应用程序之间的中间层......
  • 2012年总结
    时间过的好快,春节马上来临了。本来计划在元月就总结下2012年,结果来了个着急的项目,推迟了总结。 2012本来是世界末日的,也是自己的本命年。 虽然自己时时小心翼翼,但总有些磕......
  • 修改他人代码经验总结
    由于工作的需要,十几年来经常要在别人的程序代码的基础上修改才能完成目标,与自己从头做项目区别还是不小,简单总结如下:1、继承无论你觉得别人的东西写得再烂,再坏,你首先也要耐......