单例模式作为入门编程人员面试必考题之一,也是被玩坏了, 猛然一搜尽然有七种写法,什么懒汉,饿汉五花八门, 这里参考已经比较不错的文章, 忽略五花八门的命名, 把单例模式不同写法按逻辑演进梳理一下, 方便记忆。

参考文章:
1. 单例模式的八种写法比较
2. Wiki: Initialization-on-demand holder idiom
3. Java Singleton Design Pattern Best Practices with Examples

单例模式的应用场景

  • 整个应用中只需要特定类型的实例需要全局唯一, 否则应用程序就没法正常运行。

单例模式的最原始写法(线程不安全)

public class Singleton
{private static Singleton instance; // can only be accessed by getInstanceprivate Singleton()  // can not called by outside to create more instances{...}public static Singleton getInstance(){if (instance == null)instance = new Singleton();return instance;}
}

单例模式的代码乍一看很简单, 但是对于第一次看到单例模式的初学者来说, 有几个细节需要关注。

  • 单例模式的成员变量instance 的修饰符必须是 private static

    • 因为静态变量是被所在类的所有实例共享的
  • 单例模式的构造函数必须用private修饰
    • 从语法层面保证其他类中,根本无法获得实例化该类的权限
  • 单例模式获取单例的方法getInstance用public static synchronized 修饰
    • public static关键字修饰的方法为其他类获得单例模式的唯一实例提供了接口

上述写法仅仅适用于不会有多个线程同时调用getInstance方法,现假设有两个线程同时调用getInstance, 假设线程A 刚刚执行完if( instance == null )后,时间片用尽, 线程B也执行到了if( instance == null )的判断, 此时线程B也会通过该判断, 至此之后, 无论CPU如何调度, 线程A和线程B都会执行一次new Instance , 从而导致线程A和线程B获得的对象是不一样的,违背了单例模式创建实例的唯一性。

所以接下来需要解决的是多线程模式下的单例模式

单例模式的线程安全写法

  • 通过synchronized关键字保证线程安全(非常简单)

public class Singleton
{
private static Singleton instance; // can only be accessed by getInstance
private Singleton() // can not called by outside to create more instances
{

}

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

}

该写法和最原始的写法相比, 仅仅多了一个synchronized 关键字, 保证了同一时刻只有一个线程可以进入getInstance方法。

  • 缺点:

    • 同一时刻只有一个线程可以执行getInstance()方法,实际上, 只要在instance 被初始化了以后, return instance 是可以被并发执行的。

单例模式的线程安全高效写法1

public class Singleton {private static volatile Singleton singleton;private Singleton() {}public static Singleton getInstance() {if (singleton == null) {synchronized (Singleton.class) {if (singleton == null) {singleton = new Singleton();}}}return singleton;}
}
  • 与上一个的线程安全写法相比,有两个需要注意的变化。

    • synchronized关键字仅仅修饰了 if(singleton == null) 以后的内容。

      • 这个变动好理解, 因为当instance 被实例化以后, return是可以并发执行的。
    • synchronized关键字修饰的块的内部又加了一次重复地判断 if(singleton == null)
      • 这个变动需要思考一下, 因为在instance 尚未被初始化时, 还是有可能有多个线程同时通过 if(singleton == null) 的判断。 例如线程A 通过了if(singleton == null) 的判断,进入了synchronized部分, 在synchronized方法执行到一半时被挂起, 线程B得到调度, 此时同样会通过 if(singleton == null) 的判断, 虽然无法立即进入synchronized块, 但是等待线程A执行完synchronized部分以后, 线程B还是会再次进入synchronized方法。
    • 成员变量的instance 修饰符多了volatile 关键字。
      • 该关键字保证了一个线程成功实例化instance 后, 该变化立刻对所有的线程可见。 具体细节可以单独查阅volatile 关键字的功用。

上述的单例模式基本上已经可以算是最优的写法了, 下面还有一种利用静态内部类实现的写法, 根本不使用同步机制,与该写法不分伯仲

单例模式线程安全高效写法2

 public class Singleton {private Singleton() {}private static class LazyHolder {private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance() {return LazyHolder.INSTANCE;}
}

这种方法利用了jvm 的类装载机制来保证线程安全, 因为静态变量的初始化是在类被加载的时候时进行的, 而jvm 加载类时, 是只允许一个线程进入的, 这样就保证了线程安全。 同时, 由于是静态内部类, 所以并不会在Singleton 被加载的时候就初始化LazyHolder, 而是当getInstance() 被调用时才会加载LazyHolder。

总结

以上的三种线程安全写法基本上涵盖了单例模式最重要的知识点,对于工程实践来说, 线程安全高效写法1线程安全高效写法2 掌握一种即可。 但是以上介绍的写法的线程安全其实都可以被反射调用所违背, 如果想避免反射调用违背线程安全, 可以采用枚举方式的线程安全写法, 但是这种考量太不常用了, 也无法实现延迟加载, 有兴趣者可以阅读参考文章3.

单例模式( Single Pattern ): 不仅仅是回字的四种写法相关推荐

  1. 回字有四种写法,阶乘verilog实现有几种方法?

    回字有四种写法,阶乘verilog实现有几种方法? 方式一:普通方式实现阶乘计算: verilog代码: module tryfact; function[31:0]factorial; input[ ...

  2. 回字有四种写法,那你知道单例有五种写法吗

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达今日推荐:2020年7月程序员工资统计,平均14357元,又跌了,扎心个人原创100W+访问量博客:点击前往,查看更多 转自 ...

  3. 【转】回字有四种写法,那你知道单例有五种写法吗

    目录导航 基本介绍 写法介绍 饿汉式 懒汉式 双重检测 内部类 枚举 总结 基本介绍 单例模式(Singleton)应该是大家接触的第一个设计模式,其写法相较于其他的设计模式来说并不复杂,核心理念也非 ...

  4. 回字的四种写法之编程

    从事java程序员一年多了,才在javaeye上发表第一篇文章,确实有点晚,之前总是觉得技术不到家,没什么发言权.今天突然想写点什么,就当是随笔了. 记得以前上学的时候学过孔乙己,一个极端无助,没有任 ...

  5. 请问“回”字有几种写法?

    我们一行10人在讨论如何提高我们学校毕业生就业竞争力的问题,突然孔乙己用一种挑衅加高傲的眼神问我:"在论坛里看你说话感觉你才高八斗,满腹经纶.现在我有一个问题想请教一下:你知道'回'字有几种 ...

  6. 回字有四样写法之引号编码

    各种引号                 Unicode 说明 Macintosh 按键 Windows 按键 Linux (X) 按键 HTML entity   垂直单引号(兼畧缩号) ' U+0 ...

  7. python:函数中五花八门的参数形式(茴香豆的『回』字有四种写法)

    毫不夸张的说,python语言中关于函数参数的使用,是我见过最为灵活的,随便怎么玩都可以,本文以数学乘法为例,演示几种不同的传参形式: 一.默认参数 def multiply1(x, y):retur ...

  8. 茴字的四种写法——浅谈移动前端适配

    1. 什么是前端适配 从UI展现层面上: 我们期望不同尺寸的设备,页面可以自适应的展示或者进行等比缩放,从而在不同的尺寸的设备下看起来协调或者差不多. 从代码实现层面上: 我们希望前端适配可以用用尽可 ...

  9. 茴字的四种写法—移动适配方案的进化

    话说我刚工作的时候,就开始用rem了,过了没多久,接触到了flexible,系统化且支持iOS的retina屏迅速征服了我,最近又看到了大漠大神的vw.所以本文想完成一篇一站式的文章,可以系统的了解前 ...

最新文章

  1. python编写脚本替换 jar包文件_shell-替换jar包中配置文件脚本
  2. UE4笔记-进程/线程/网络/IO模块的相关问题记录
  3. java模式匹配_用Java匹配模式
  4. 动态调整线程池_调整线程池的重要性
  5. Docker教程小白实操入门(19)--如何通过inspect指令查看数据卷的信息
  6. 公客网beta阶段发布说明
  7. 蚂蚁课堂视频笔记+笔记对应源码+思维导图下载地址
  8. URL和URI的区别与联系
  9. 远不止三色,图片被压扁了,用Python的Tkinter做一个既高颜值又好用的计算器
  10. 图床,全网最稳定的免费图床
  11. 项目质量管理工具--鱼骨图(石川图)
  12. [sphinx]中文语言模型训练
  13. Kali LInux 下Osmocom-BB开源项目
  14. 【业务架构】价值链分析的直接指南
  15. 图文详解!10大高性能开发核心技术+
  16. C语言简明教程,C语言简明教程
  17. TELEDYNE DALSA Sherlock 系统软件 sherlock SH7-PRO视觉加密狗
  18. B-spline三次B样条曲线方程
  19. STM32 MCUISP下载 开始连接...401, 接收到: 芯片超时无应答,无法连接
  20. taobao.top.oaid.decrypt( OAID解密 )

热门文章

  1. 老男孩python全栈s21day03作业
  2. MySQL廖雪峰的官方网站
  3. 视频教程-嵌入式读图基础-智能硬件
  4. 为什么计算机技术用云来形容,云雨一词,到底是啥意思?为何古人用云雨来形容房事...
  5. android每天定时打卡,钉钉定时打卡脚本下载|叉叉助手钉钉定时打卡插件安卓版下载 v4.3.1 - 跑跑车安卓网...
  6. java 反射无参方法_无参构造方法 ? 反射
  7. python代码中的中文语法错误:SyntaxError: Non-ASCII character ‘\xe5‘ in file trade.py on line 7
  8. 穿搭网在线头像试衣间
  9. 面对无秘,假设你是微信的产品经理浅析
  10. 一款方便好用的免费截图神器