• 你真的懂单例模式么


    这里讲解单例模式中的懒汉模式和饿汉模式的注意事项及代码实现

    目录

    单例模式

    饿汉模式

    懒汉模式

    匿名内部类实现单例模式

    使用枚举来实现单例模式


    单例模式

    单例模式就是只能有唯一一个实例

    饿汉模式

    什么是饿汉模式呢??

    从字面理解上,一个饿汉很着急迫不及待的想吃饭.

    举个栗子,如果进行读取10个G的文件,要是用饿汉模式就是一次性的将10个G的文件全部加载完成才可以看.  ---->很着急 , 迫不及待 

    饿汉模式从类加载开始就创建出一个实例--->(很早的就创建实例-->立即加载)

    代码实现:

    1. //饿汉模式
    2. public class Singleton {
    3. //创建私有静态实例,让类加载时就创建出实例-->饿汉模式
    4. private static Singleton instance = new Singleton();
    5. private Singleton(){
    6. //将构造方法设置为私有,保证其他类不能实例化对象(new实例)
    7. }
    8. //构造方法返回实例
    9. public Singleton getInstance(){
    10. return instance;
    11. }
    12. }
    • 将instance利用static->从类加载的时候就创建出一个对象.
    • 构造方法设置为私有不能让其他类再次new对象
    • 返回实例

    懒汉模式

    懒汉模式与饿汉模式相反

    懒汉模式 很缓慢 ~~ 不急迫~~

    还是举个读取文件的栗子~~

    还是要读取10个G的文件,懒汉模式是加载屏幕所能容纳的范围,能让用户看到的范围,当翻到下一张的时候,再次进行加载  -->效率很高->用多少~~~加载多少~~~

    懒汉模式是 等待需要创建实例时在进行创建--->延迟加载

    懒汉模式代码实现:

    1. //实现懒汉模式
    2. public class Singleton {
    3. //1.使用volatile保证内存可见性和禁止指令重排序
    4. //2.先将对象初始化为null,当需要实例化对象时在进行创建
    5. private volatile static Singleton instance = null;
    6. //设置构造方法
    7. private Singleton(){
    8. //私有构造方法,防止其他类实例化对象
    9. }
    10. //获取到实例
    11. //1.使用双重if是为了降低竞争锁的概率
    12. public Singleton getInstance(){
    13. //外层if是用来判断实例有没有初始化好?
    14. //1.如果已经初始化,不需要竞争锁,不进入if,直接往下执行
    15. //2.如果没有初始化尝试竞争锁
    16. if(instance==null){
    17. //使用synchronized原因是
    18. //1.防止多个线程同时创建,将读和写打包成一个原子->保证原子性,让其线程竞争锁
    19. synchronized(Singleton.class){
    20. //里层if: 当线程拿到锁之后,再来判断是否要真正的创建实例
    21. if(instance==null){
    22. instance = new Singleton();//实例化对象
    23. }
    24. }
    25. }
    26. return instance;
    27. }
    28. }

    使用懒汉模式要注意三点

    • 加锁
    • 加上volatile关键字
    • 双重if锁定

    加锁的目的

    因为在创建实例时有写线程在读,有些线程在写,所以很可能导致非原子操作,我们就把读和写打包成1个一个原子-->保证原子性,此时多个线程就要竞争锁

    加上volatile关键字的作用

    • 保证内存可见性

    因为有的线程在读有的线程在写,很有可能编译器将其优化成读取寄存器,使得当一个线程修改,而每次读取时不能读取最新值,所以加上volatile

    • 禁止指令重排序

    因为要创建实例包含三个操作1.申请内存 2. 在内存上构造出实例 3.将地址赋值给对象的引用

    编译器优化时,将2,3顺序颠倒,导致实例化出一个无效的对象,所以加上volatile关键字

    双重if的目的

    双重if最核心的是降低锁的竞争

    里层if是保证实例唯一性,当线程拿到锁之后,判断是否要真正的创建实例,-->只能创建一次

    外层if

    • 保证是否已经创建好实例,如果已经创建好实例,就已经是线程安全,不要再次加锁,当线程安全时在进行加锁,会导致CPU速率下降,很可能造成堵塞,所以如果已经创建好实例,不用再次加锁已经是线程安全.
    • 如果没有创建好实例,就尝试竞争锁

    匿名内部类实现单例模式

    1. public class InnerClassSingleton {
    2. private InnerClassSingleton(){
    3. //私有构造方法,防止其他类new对象
    4. }
    5. //创建内部类
    6. private static final class SingletonHolder{
    7. private static final InnerClassSingleton instance = new InnerClassSingleton();
    8. }
    9. //返回实例
    10. public InnerClassSingleton getInstance(){
    11. return SingletonHolder.instance;
    12. }
    13. }

    使用枚举来实现单例模式

    1. public enum EnumSingleton {
    2. instance;
    3. public EnumSingleton getInstance(){
    4. return instance;
    5. }
    6. }
  • 相关阅读:
    数据挖掘算法原理与实践:k-均值
    python包合集-argparse
    spring中的注解事务的演示及添加步骤
    【云原生之kubernetes实战】在k8s环境下部署Affine知识库工具
    markdown文档:一个简单标记语言的使用及实际应用(超简单)
    任意文件下载
    linux centos安装远程软件向日葵
    【计算机毕业设计】基于jsp网上书店(源代码+论文)
    rtl 开发必会技能
    【企业安全】企业安全系列第 2 部分 — 身份和访问管理
  • 原文地址:https://blog.csdn.net/m0_61210742/article/details/126101310