单例模式有的时候特别重要,因为某些系统是要求某个类在整个生命周期中有且只有一个实例存在,这时候就要用到单例模式。

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

我们一步步研究。

按照惯例,先讲故事。

各个大学想请史上最牛科学家来自己学校讲课。

分析一波,既然是史上最牛科学家,那么就代表只有一个人,因为并没有说“之一”二字,带了“之一”的话那就是一批人。所以这个人假设就叫Edward,就是史上最牛科学家的唯一实例。各个大学现在要来抢他,不可能把他拆了,必须按照人家的档期来安排。

那么到底如何请呢?

请史上最牛科学家套路第一版

private static BestScientist edward = null;
private BestScientist(){}
public static BestScientist getInstance(){
if(edward == null){
edward = new BestScientist();
}
return edward;
}

我们观察,上面单例模式的定义说得清楚,只提供一个访问它的全局访问点,那么其他内部的东西都要被封装。所以edward作为静态变量是private的。只留了一个getInstance()方法作为全局访问点。

要注意,把构造函数重写改为private,将外部直接创建实例的机会堵住。

为什么是static?

因为只有变量定义为static,它才是属于类的,而不是属于某个实例,它才在类加载的时候就能被初始化,这个变量才是唯一的。

而方法只有是静态的,才可以直接使用类去调用而不必先创建实例。

BestScientist.getInstance();// Edward就来了。

缺陷:

不能满足并发请求,当一个线程进来的时候刚判断完Edward是空,这时候另一个线程却刚刚完成创建,那么第一个线程因为已经过了空值判断的约束,就会再创建一个实例。那就乱了,相当于出来了一个Edward的克隆人。

请史上最牛科学家套路第二版

private static BestScientist edward = new BestScientist();
private BestScientist(){}
public static BestScientist getInstance(){
return edward;
}

这个版本也叫饿汉版,就是着急,类加载的时候就把唯一实例创建完了,其他人来用的时候就过来取就行了,系统永远不会出现多个实例。

请史上最牛科学家套路第三版

private static BestScientist edward = null;
private BestScientist(){}
synchronized public static BestScientist getInstance(){
if(edward == null){
edward = new BestScientist();
}
return edward;
}

也叫懒汉式,就是不着急,实例的创建被延迟到使用的时候,正如我们在浏览器中滑到那个位置才开始加载一样,这样可以减少系统负担。这基本就是第一版的并发版本,加了把锁强制多线程时单个线程获得锁独享操作,其他线程等待。锁的位置也可以移到方法体内部。

private static BestScientist edward = null;
private BestScientist(){}
public static BestScientist getInstance(){
if(edward == null){
synchronized(BestScientist.class){
edward = new BestScientist();
}
}
return edward;
}

貌似更精确,但是问题又出现了,当一个线程获得锁开始创建实例的时候,另外好几个线程都经过了null判断阻塞在马上创建实例的前面,一旦锁被释放,将创建多个实例出来。

private static BestScientist edward = null;
private BestScientist(){}
public static BestScientist getInstance(){
if(edward == null){
synchronized(BestScientist.class){
if(edward == null){
edward = new BestScientist();
}
}
}
return edward;
}

锁内再加一层null判断即可解决上面的问题。这就是Double-check Locking。懒汉式的最终版本。

请史上最牛科学家套路第四版

我们发现饿汉式和懒汉式各有利弊,饿汉式非常简单,但是占用了系统资源,而懒汉式虽然可以做到用的时候再创建,但是需要很多判断,还有锁机制,会影响性能。那么下面将使用的是饿汉懒汉合二为一,克服了两者各自的缺点,集成了两者的优点,那就是IoDH(在军需官那里初始化),军需官就是ClassHolder。

private BestScientist(){}
private static class ClassHolder{
private ClassHoder(){
private final static BestScientist edward = new BestScientist();
}
}
public static BestScientist getInstance(){
return ClassHolder.edward;
}

外部仍旧是通过

BestScientist.getInstance();// Edward就来了。

获取实例,但是内部实现却大不一样。这里去掉了锁,增加了一个static class,这个类的构造函数里定义了一个静态变量并初始化创建实例。最终效果是,当BestScientist类加载时,其静态内部类ClassHolder并没有任何动作,ClassHolder是什么时候加载的呢?是在getInstance方法中获取ClassHolder内部静态变量的时候加载,而随着ClassHolder被加载,其静态变量会跟着一起初始化。这可以程得上完美,尽取以上饿汉懒汉优势而摒弃双方劣势。

为什么是static class?

static关键字有两个特性,上面已经提到,这里再重申一次

  • static定义的变量属于类,可以直接类名调用。
  • static修饰的内容随着类加载就会被运行。

static所有针对的内容都是类,它是属于类的而不是实例。所以,反过来说,类被加载的时候就会同时运行static修饰的内容,类加载是在类被使用的时候,使用完了以后会按照GC机制清除。


扩展

  • 单例模式限制了内存中仅存一个实例,外部仅通过一个公共访问点使用。降低了内存的消耗,支撑了业务中一些必要的需求。

  • 可以稍作改造,根据业务需要以及机器性能,将内存中的实例个数上升到两个或者一个固定的个数,这样在某个单例被过度使用的时候,可以被分担一些压力。

问题

有些人可能觉得单例类的职责过重,既有工厂角色又有产品角色,但我不这么认为,单例类的内部结构在IoDH就趋于非常稳定的状态,不会有什么扩展,所以不必去增加抽象层,而它的职责就是提供唯一实例,这是个高内聚的类,所以我认为并不违反“单一职责原则”,就看你在哪一个层面看问题了。

注意

上面提到了类和类的实例在使用完毕以后,会按照GC机制去清除,那么可能会造成共享实例被清除的后果。但是GC默认的机制是按照使用计数器,当这个实例没有任何线程去使用的时候,它的清除级别也是较低的,及时被清除了,再在使用的时候去创建也未尝不可,这比起其他版本的开销来说已经非常高效。

精雕细琢——全方位解析单例模式相关推荐

  1. C++对象确定性解析单例模式

    该博文为原创文章,未经博主同意不得转载,如同意转载请注明博文出处 本文章博客地址:https://cplusplus.blog.csdn.net/article/details/105163495 解 ...

  2. 什么是无线网桥?全方位解析无线网桥及应用场景

    什么是无线网桥?全方位解析无线网桥及应用场景 无线网桥是什么?无线网桥只能做无线监控系统吗?除了无线监控系统还能做什么?如何选择无线网桥?以上的疑问你是否都能解答?如果不能,说明你对无线网桥的认识还不 ...

  3. 360度全方位解析死链接

    相信业内人士都知道什么叫做死链接,在这里我再赘述一下, 死链接指的就是失效的链接.错误链接,它原本是正常的,但是后来就变成无效的链接,使得网页中打开这个死链接地址,服务器回应的就是打不开的页面或友好的 ...

  4. 用python全方位解析2019新冠疫情词云图

    用python全方位解析2019新冠疫情词云图 wordcloud模块 jieba分词器模块 PyCharm创建工程 生成词云的方法 wordcloud.WordCloud()的参数介绍 影响词云图像 ...

  5. 全方位解析俄语系勒索软件的生态系统

    本文讲的是全方位解析俄语系勒索软件的生态系统, 勒索软件的发家史 毫无疑问,近两年,以敲诈勒索为目的的文件加密恶意软件逐渐成为恶意软件中的主力军,勒索软件是当今网络攻击中一种最主要的攻击工具,对政府组 ...

  6. ots在线考计算机的word,全方位解析弘成OTS在线考试系统

    原标题:全方位解析弘成OTS在线考试系统 弘成·OTS在线考试系统 覆盖在线考试全过程 满足多种业务场景的智慧考试系统 10年品质,让考试更智慧! 6大优势,轻松解决考试痛点 01智能题库 支持多种试 ...

  7. Android基础——JSON数据的全方位解析

    Android基础--JSON数据的全方位解析 本篇文章包括以下内容: JSON是什么 JSONObject的解析和存储 JSONObject的解析和存储(抽象) JSONArray的解析和存储 模拟 ...

  8. 微信小程序全方位解析

    九个月之前,应用号首次被提出.近日,应用号以"小程序"的名称,正式向200人发出公测邀请."无需下载,用完即走",微信小程序提供了一种新的开放能力,使其在IT圈 ...

  9. 【笔记-vue】《imooc-vue.js高仿饿了么》、《imooc-vue 音乐app》、《imooc-vue.js源码全方位解析》

    20170709 - 20171128:<imooc-vue.js高仿饿了么> 一.第一章 课程简介 1-1课程简介 1.需求分析-脚手架工具-数据mock-架构设计-代码编写-自测-编译 ...

最新文章

  1. HADOOP2单机版
  2. 子弹短信新发布,支付宝即将入驻
  3. ALSA(二), makefile, Autotools, premake
  4. php矢量瓦片,矢量瓦片相关计算函数
  5. python操作mongodb语法_python操作mongodb怎么找到所有的集合
  6. 极客大佬用什么电脑_极客特惠:笔记本电脑,高清电视和免费应用
  7. oracle分页查询加总数,oracle count 百万级 分页查询记要总数、总条数优化
  8. 好气!等一年,这个iCloud 账户漏洞竟只值$1.8万?!说好的$35万呢???
  9. ERP计划参数如何在线更新
  10. CSS3实现轮播图效果
  11. oracle9i如何卸载,如何卸载oracle 9i
  12. Eclipse SVN 项目后转换成Maven项目
  13. gif动图怎么制作?怎么截取视频做成gif动图?
  14. 海康摄像头拍照(java版,拿走即用)
  15. 春节宅家玩什么?8款小游戏陪你一起过年
  16. X11 xcb 交叉编译
  17. linux上使用drive从google drive 下载文件和文件夹
  18. lighttpd 之九 配置信息加载
  19. 简单分析RTMP规范
  20. Phonetic symbol 单元音 - 短元音 ɒ(新)/ ɔ(旧) 与 长元音 ɔː

热门文章

  1. 《亮剑》太棒了...
  2. 操作系统——银行家算法(银行家和房地产开发商的爱恨情仇)
  3. 同花顺_代码解析_技术指标_B
  4. 前端开发常用技术工具网址导航汇总
  5. windows驱动在线帮助
  6. Centos7 -- glibc 升级失败、意外删除、故意删除后的处理方法
  7. upload-labs靶场-pass-14
  8. 寻找 DeFi 保险的合理保费
  9. Go学习(二十):启动HTTP服务的方式
  10. java/php/net/python汽车租赁网系统设计