单例模式的几种实现

2023-11-10

单例模式的几种实现

在开发中,我们总是会遇到使用单例模式的情况,今天就来总结一下几种实现单例模式的方法。
1.饿汉式

public class SingletonDemo1 {
    //类初始化时,立即加载该对象(没有延时加载的优势)!由于加载类时,天然的线程安全!
    private static SingletonDemo1 instance = new SingletonDemo1();
    //私有构造器
    private SingletonDemo1(){}
    //方法没有同步,调用效率高
    public static SingletonDemo1 getInstance(){
        return instance;
    }
}

不管是否使用到该类,都先初始化该类,有可能会造成资源浪费!
2.懒汉式, 有两种实现方式,
a).方式一

public class Singleton {
    private  static Singleton singleton;
    public Singleton() {
    }

    public static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

这种写法只能在单线程下使用。如果是多线程,可能发生一个线程通过并进入了 if (singleton == null) 判断语句块,但还未来得及创建新的实例时,另一个线程也通过了这个判断语句,两个线程最终都进行了创建,导致多个实例的产生。所以在多线程环境下必须摒弃此方式。
这种写法线程不安全。
b).加入同步锁

public class SingletonDemo2 {
    // 类初始化时,不初始化这个对象(延时加载,懒加载)
    private static SingletonDemo2 instance;
    // 私有构造器
    private SingletonDemo2() {
    }
    // 方法同步,调用效率低
    public static synchronized SingletonDemo2 getInstance() {
        if (instance == null) {
            instance = new SingletonDemo2();
        }
        return instance;
    }
}

通过为 getInstence() 方法增加 synchronized 关键字,迫使每个线程在进入这个方法前,要先等候别的线程离开该方法,即不会有两个线程可以同时进入此方法执行 new SingletonDemo2(),从而保证了单例的有效。但它的致命缺陷是效率太低了,每个线程每次执行 getInstance() 方法获取类的实例时,都会进行同步。而事实上实例创建完成后,同步就变为不必要的开销了,这样做在高并发下必然会拖垮性能。所以此方法虽然可行但也不推荐。
网上这样的代码更多,可以很好的工作,但是缺点是效率低。
3. 双重检查锁式。
实际上,早在JDK1.5就引入volatile关键字,所以又有了一种更好的双重校验锁写法。

public class Singleton {
    private volatile static Singleton singleton;
    public Singleton() {
    }

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

DCL体现在进行了两次 if (singleton == null) 的检查,这样既同步代码块保证了线程安全,同时实例化的代码也只会执行一次,实例化后同步操作不会再被执行,从而效率提升很多。
双重检查锁定(DCL)方式也是延迟加载的,它唯一的问题是,由于 Java 编译器允许处理器乱序执行,在 JDK 版本小于 1.5 时会有 DCL 失效的问题。当然,现在大家使用的 JDK 普遍都已超过 1.4,只要在定义单例时加上 1.5 及以上版本具体化了的 volatile 关键字,即可保证执行的顺序,从而使单例起效。
Android 中鼎鼎大名的 Universal Image Loader 和 EventBus 都是采用了这种方式的单例
4).静态内部类式

public class SingletonDemo3 {
    //静态内部类
    private static class SingletonClassGetInstance {
        public static final SingletonDemo3 instance = new SingletonDemo3();
    }
    // 私有构造器
    private SingletonDemo3() {
    }
    public static  SingletonDemo3 getInstance() {
        return SingletonClassGetInstance.instance;
    }
}

这种方式是SingletonDemo3 类被装载了,instance不一定被初始化。因为SingletonClassGetInstance 类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonClassGetInstance 类,从而实例化instance。
5). 枚举式模式

public enum SingletonDemo4 {
    //这个枚举对象本身就是单例对象
    INSTANCE;
    //添加自己需要的操作
    public void operation(){
    }
}

这种模式下,线程安全,调用效率高,但是不能延时加载!实际工作中,少有人使用这种方式!难道是因为太高级了!O(∩_∩)O~
小结
a. 单例模式有五种实现方式
1).饿汉式
线程安全,调用效率高,但是不能延时加载
2).懒汉式
线程安全,调用效率不高,并且可以延时加载
3).双重检查锁式
由于JVM底层内部模型原因,偶尔会出问题,不建议使用
4).静态内部类式
线程安全,调用效率高,并且可以延时加载。推荐使用
5).枚举式
线程安全,调用效率高,不能延时加载,但是可以天然的防止反射和反序列化漏洞
我们在编码中,有关单例模式的具体实现方式,需要根据实际情况来选择!

b. 单例模式中的存在的问题
如果单例类实现了java.io.Serializable接口,那么这个类可能会被反序列化,并且反序列化多次同一对象时,会得到多个单例类的实例。这样就不是单例了!
解决办法,需要在单例类中添加如下方法,


    // 反序列化时,如果定义了readResovle() 则直接返回此方法指定的对象,而不需要单独再创建新对象!
    private Object readResovle() throws ObjectStreamException {
        // TODO Auto-generated method stub
        return instance;
    }

PS:在Android中使用单例模式可能会内存泄露。
当调用getInstance时,如果传入的context是Activity的context。只要这个单例没有被释放,那么这个Activity也不会被释放一直到进程退出才会释放。

public class CommUtils {

    private volatile static CommUtils mCommUtils;

    private Context mContext;
    public CommUtils(Context context) {
        mContext=context;
    }

    public static  CommUtils getInstance(Context context) {
        if (mCommUtils == null) {
            synchronized (CommUtils.class) {
                if (mCommUtils == null) {
                    mCommUtils = new CommUtils(context);
                }
            }
        }
        return mCommUtils;
    }
}

解决办法
能使用Application的Context就不要使用Activity的Content,Application的生命周期伴随着整个进程的周期

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

单例模式的几种实现 的相关文章

  • 按键时关闭 ModalWindow

    我希望能够在用户按下某个键 在我的例子中是 ESC 时关闭 ModalWindow 我有一个用于按键的 Javascript 侦听器 它调用取消按钮 ID 的单击事件 jQuery modalWindowInfo closeButtonId
  • 如何让 BlazeDS 忽略属性?

    我有一个 java 类 它有一个带有 getter 和 setter 的字段 以及第二对 getter 和 setter 它们以另一种方式访问 该字段 public class NullAbleId private static final
  • 在内存中使用 byte[] 创建 zip 文件。 Zip 文件总是损坏

    我创建的 zip 文件有问题 我正在使用 Java 7 我尝试从字节数组创建一个 zip 文件 其中包含两个或多个 Excel 文件 应用程序始终完成 没有任何异常 所以 我以为一切都好 当我尝试打开 zip 文件后 Windows 7 出
  • Java 枚举与创建位掩码和检查权限的混淆

    我想将此 c 权限模块移植到 java 但是当我无法将数值保存在数据库中然后将其转换为枚举表示形式时 我很困惑如何执行此操作 在 C 中 我创建一个如下所示的枚举 public enum ArticlePermission CanRead
  • 如何循环遍历所有组合,例如48 选择 5 [重复]

    这个问题在这里已经有答案了 可能的重复 如何在java中从大小为n的集合中迭代生成k个元素子集 https stackoverflow com questions 4504974 how to iteratively generate k
  • 如何更改javaFX中按钮的图像?

    我正在使用javaFX 我制作了一个按钮并为此设置了图像 代码是 Image playI new Image file c Users Farhad Desktop icons play2 jpg ImageView iv1 new Ima
  • java.lang.IllegalStateException:提交响应后无法调用 sendRedirect()

    这两天我一直在尝试找出问题所在 我在这里读到我应该在代码中添加一个返回 我做到了 但我仍然得到 java lang IllegalStateException Cannot call sendRedirect after the respo
  • 在 junit 测试中获取 javax.lang.model.element.Element 类

    我想测试我的实用程序类 ElementUtils 但我不知道如何将类作为元素获取 在 AnnotationProcessors 中 我使用以下代码获取元素 Set
  • Java ResultSet 如何检查是否有结果

    结果集 http java sun com j2se 1 4 2 docs api java sql ResultSet html没有 hasNext 方法 我想检查 resultSet 是否有任何值 这是正确的方法吗 if resultS
  • 如何在谷歌地图android上显示多个标记

    我想在谷歌地图android上显示带有多个标记的位置 问题是当我运行我的应用程序时 它只显示一个位置 标记 这是我的代码 public class koordinatTask extends AsyncTask
  • 在我的 Spring Boot 示例中无法打开版本 3 中的 Swagger UI

    我在 Spring Boot 示例中打开 swagger ui 时遇到问题 当我访问 localhost 8080 swagger ui 或 localhost 8080 root api name swagger ui 时出现这种错误 S
  • Eclipse 选项卡宽度不变

    我浏览了一些与此相关的帖子 但它们似乎并不能帮助我解决我的问题 我有一个项目 其中 java 文件以 2 个空格的宽度缩进 我想将所有内容更改为 4 空格宽度 我尝试了 正确的缩进 选项 但当我将几行修改为 4 空格缩进时 它只是将所有内容
  • 如何使用 jUnit 将测试用例添加到套件中?

    我有 2 个测试类 都扩展了TestCase 每个类都包含一堆针对我的程序运行的单独测试 如何将这两个类 以及它们拥有的所有测试 作为同一套件的一部分执行 我正在使用 jUnit 4 8 在 jUnit4 中你有这样的东西 RunWith
  • 专门针对 JSP 的测试驱动开发

    在理解 TDD 到底是什么之前 我就已经开始编写测试驱动的代码了 在没有实现的情况下调用函数和类可以帮助我以更快 更有效的方式理解和构建我的应用程序 所以我非常习惯编写代码 gt 编译它 gt 看到它失败 gt 通过构建其实现来修复它的过程
  • 最新的 Hibernate 和 Derby:无法建立 JDBC 连接

    我正在尝试创建一个使用 Hibernate 连接到 Derby 数据库的准系统项目 我正在使用 Hibernate 和 Derby 的最新版本 但我得到的是通用的Unable to make JDBC Connection error 这是
  • 如何使用mockito模拟构建器

    我有一个建造者 class Builder private String name private String address public Builder setName String name this name name retur
  • 使用反射覆盖最终静态字段是否有限制?

    在我的一些单元测试中 我在最终静态字段上的反射中遇到了奇怪的行为 下面是说明我的问题的示例 我有一个基本的 Singleton 类 其中包含一个 Integer public class BasicHolder private static
  • 长轮询会冻结浏览器并阻止其他 ajax 请求

    我正在尝试在我的中实现长轮询Spring MVC Web 应用程序 http static springsource org spring docs 2 0 x reference mvc html但在 4 5 个连续 AJAX 请求后它会
  • 双枢轴快速排序和快速排序有什么区别?

    我以前从未见过双枢轴快速排序 是快速排序的升级版吗 双枢轴快速排序和快速排序有什么区别 我在 Java 文档中找到了这个 排序算法是双枢轴快速排序 作者 弗拉基米尔 雅罗斯拉夫斯基 乔恩 本特利和约书亚 布洛赫 这个算法 在许多数据集上提供
  • Java中super关键字的范围和使用

    为什么无法使用 super 关键字访问父类变量 使用以下代码 输出为 feline cougar c c class Feline public String type f public Feline System out print fe

随机推荐

  • IP总数,子网掩码查询表

    IP总数 子网掩码查询表 不同位数的网络号对应的子网以及主机数
  • 刷脸支付面向全国诚招实力合作商政策置顶

    刷脸支付在各大商店 餐馆逐渐铺开 消费者在购物付款时 不用打开手机二维码 只是看一眼支付设备就能轻松完成付款 从到地方的媒体 都在关注 刷脸支付 甚至会认为刷脸支付会代替现在的流行的二维码支付 成为未来支付的主流 刷脸支付正在掀起新零售新浪
  • 2022人生除生死外,无大事——一边治愈,一边前行!

    今天看完了 人生大事 这个电影 灵动的小姑娘 痞帅的朱一龙 一个殡葬师的生活在这个小姑娘的闯入后 开始有了波澜 活力 他逐渐找到了生活的意义和方向 挺好评的一部电影 虽然是关于离别 死亡 却是在很欢快嘈杂的情境下穿插着的 有泪有笑有温情 人
  • NRF24L01 一对三通信

    注意 NRF24L01 无论是一对一通信 还是一对多通信 其实本质上同一时刻是一对一通信 它是属于半双工通信 你只需要先调通一对一的通信 无论是一对2 一对六也是同一个原理 基础SPI知识和NRF24L01基础知识不多说 一 项目背景 1
  • jupyter lab使用

    记录一下使用过程中遇到的问题以及解决方法 网上资料感觉说的不是很明白 还是截图好用 增加行序号 自动换行 折叠代码 查找及替换 malplotlib 增加行序号 自动换行 折叠代码 打开setting 进入setting editor页面
  • xtrabackup 全量备份、恢复数据

    1 全量备份 root localhost lib innobackupex defaults file defaults file user mysql username password mysql password stream ta
  • seaborn.distplot()

    参考https www cntofu com book 172 docs 24 md 一 函数 seaborn distplot a bins None hist True kde True rug False fit None hist
  • 在家干什么副业好?工作之余做什么副业比较好

    现在真的到了一个副业时代 人人都可以做副业 不仅可以增加一份收入 还可以拓宽视野 增加阅历 如果是在家里做副业的话 那么还是推荐网络短视频 直播和写作 这也是普通人最容易上手的副业 1 拍游戏视频 既然大家这么喜欢玩游戏 那就把平时刷视频的
  • layui的layer弹出层和form表单

    参考文献 含代码 http www shagua wiki project 3 p 113
  • 【Git】如何在Vscode中使用码云(Gitee)实现远程代码仓库与本地同步?(新手图文教程)

    一 安装git 直接参照下面的链接就OK Git Git的下载 安装与配置教程 图文 敦厚的曹操的博客 CSDN博客 git下载教程 Git Git的下载与安装教程 图文https blog csdn net dxnn520 article
  • 高防CDN和高防IP防护的不同之处

    网站的运行离不开服务器 服务器在使用的过程中不可避免的会遇到一些攻击 需要做好相应的安全防护 那么 在安全防护上 服务器使用CDN与高防IP具体有什么不同之处呢 一 IP数量 CDN是一组ip的防护 是共享的ip 高防ip独享的ip防护 二
  • 2021-10-17 stm32f103 睡眠模式唤醒

    1 开启 睡眠时钟 2 打开 WFI 3 GPIO 1 GPIO内部电路图 下面转载 https blog csdn net xiewenhao12 article details 89030398 1 根据设备原理图查看IO外部引脚连接电
  • 13.108.Spark 优化、Spark优化与hive的区别、SparkSQL启动参数调优、四川任务优化实践:执行效率提升50%以上

    13 108 Spark 优化 1 1 25 Spark优化与hive的区别 1 1 26 SparkSQL启动参数调优 1 1 27 四川任务优化实践 执行效率提升50 以上 13 108 Spark 优化 1 1 25 Spark优化与
  • pluto实现分析(5)

    本文档的Copyleft归yfydz所有 使用GPL发布 可以自由拷贝 转载 转载时请保持文档的完整性 严禁用于任何商业用途 msn yfydz no1 hotmail com 来源 http yfydz cublog cn 7 内核接口
  • ABAP 发邮件(三)

    转自http blog sina com cn s blog 7c7b16000101bnxk html SAP ABAP 发邮件方法三 OO Report ZSENDEMAIL08 REPORT zsendemail08 CONSTANT
  • R:数据对象及类型

    数据对象 R语言创建和控制的实体被称为对象 object 它们可以说变量 数组 字符串 函数或者其它通过这些实体定义的更一般的结构 structures 在R语言里 对象是通过名字创建和保存的 R对象的名称必须以一个英文字母打头 并由一串大
  • 【Vue】动态监听localStorage或sessionStorage中数据的变化:

    1 需求 一个页面数据变化要改变另一个页面 甚至整个项目的数据变化 2 在main js中 description 监听本地储存的值变化 param number type 1 localStorage 2 sessionStorage p
  • QT在vs2019上配置流程

    一 安装VS2019 2 安装Qt5 15 VS2019只能安装QT5 14版本以上 5 15版本以上需要在在线安装平台下载安装 在QT官网https download qt io 中找到official releases gt onlin
  • Anaconda安装深度学习框架(TensorFlow,Pytorch)教程

    有道云链接 1 用anaconda创建虚拟环境 在这之前 我们需要确定服务器上安装了anaconda 使用下面命令查看 whereis anaconda anaconda data softwares opt anaconda2 bin a
  • 单例模式的几种实现

    单例模式的几种实现 在开发中 我们总是会遇到使用单例模式的情况 今天就来总结一下几种实现单例模式的方法 1 饿汉式 public class SingletonDemo1 类初始化时 立即加载该对象 没有延时加载的优势 由于加载类时 天然的