设计模式之美
王争
前Google工程师,《数据结构与算法之美》专栏作者
立即订阅
20015 人已学习
课程目录
已更新 46 讲 / 共 100 讲
0/6登录后,你可以任选6讲全文学习。
开篇词 (1讲)
开篇词 | 一对一的设计与编码集训,让你告别没有成长的烂代码!
免费
设计模式学习导读 (3讲)
01 | 为什么说每个程序员都要尽早地学习并掌握设计模式相关知识?
02 | 从哪些维度评判代码质量的好坏?如何具备写出高质量代码的能力?
03 | 面向对象、设计原则、设计模式、编程规范、重构,这五者有何关系?
设计原则与思想:面向对象 (11讲)
04 | 理论一:当谈论面向对象的时候,我们到底在谈论什么?
05 | 理论二:封装、抽象、继承、多态分别可以解决哪些编程问题?
06 | 理论三:面向对象相比面向过程有哪些优势?面向过程真的过时了吗?
07 | 理论四:哪些代码设计看似是面向对象,实际是面向过程的?
08 | 理论五:接口vs抽象类的区别?如何用普通的类模拟抽象类和接口?
09 | 理论六:为什么基于接口而非实现编程?有必要为每个类都定义接口吗?
10 | 理论七:为何说要多用组合少用继承?如何决定该用组合还是继承?
11 | 实战一(上):业务开发常用的基于贫血模型的MVC架构违背OOP吗?
12 | 实战一(下):如何利用基于充血模型的DDD开发一个虚拟钱包系统?
13 | 实战二(上):如何对接口鉴权这样一个功能开发做面向对象分析?
14 | 实战二(下):如何利用面向对象设计和编程开发接口鉴权功能?
设计原则与思想:设计原则 (12讲)
15 | 理论一:对于单一职责原则,如何判定某个类的职责是否够“单一”?
16 | 理论二:如何做到“对扩展开放、修改关闭”?扩展和修改各指什么?
17 | 理论三:里式替换(LSP)跟多态有何区别?哪些代码违背了LSP?
18 | 理论四:接口隔离原则有哪三种应用?原则中的“接口”该如何理解?
19 | 理论五:控制反转、依赖反转、依赖注入,这三者有何区别和联系?
20 | 理论六:我为何说KISS、YAGNI原则看似简单,却经常被用错?
21 | 理论七:重复的代码就一定违背DRY吗?如何提高代码的复用性?
22 | 理论八:如何用迪米特法则(LOD)实现“高内聚、松耦合”?
23 | 实战一(上):针对业务系统的开发,如何做需求分析和设计?
24 | 实战一(下):如何实现一个遵从设计原则的积分兑换系统?
25 | 实战二(上):针对非业务的通用框架开发,如何做需求分析和设计?
26 | 实战二(下):如何实现一个支持各种统计规则的性能计数器?
设计原则与思想:规范与重构 (11讲)
27 | 理论一:什么情况下要重构?到底重构什么?又该如何重构?
28 | 理论二:为了保证重构不出错,有哪些非常能落地的技术手段?
29 | 理论三:什么是代码的可测试性?如何写出可测试性好的代码?
30 | 理论四:如何通过封装、抽象、模块化、中间层等解耦代码?
31 | 理论五:让你最快速地改善代码质量的20条编程规范(上)
32 | 理论五:让你最快速地改善代码质量的20条编程规范(中)
33 | 理论五:让你最快速地改善代码质量的20条编程规范(下)
34 | 实战一(上):通过一段ID生成器代码,学习如何发现代码质量问题
35 | 实战一(下):手把手带你将ID生成器代码从“能用”重构为“好用”
36 | 实战二(上):程序出错该返回啥?NULL、异常、错误码、空对象?
37 | 实战二(下):重构ID生成器项目中各函数的异常处理代码
设计原则与思想:总结课 (3讲)
38 | 总结回顾面向对象、设计原则、编程规范、重构技巧等知识点
39 | 运用学过的设计原则和思想完善之前讲的性能计数器项目(上)
40 | 运用学过的设计原则和思想完善之前讲的性能计数器项目(下)
设计模式与范式:创建型 (2讲)
41 | 单例模式(上):为什么说支持懒加载的双重检测不比饿汉式更优?
42 | 单例模式(中):我为什么不推荐使用单例模式?又有何替代方案?
不定期加餐 (3讲)
加餐一 | 用一篇文章带你了解专栏中用到的所有Java语法
加餐二 | 设计模式、重构、编程规范等相关书籍推荐
春节特别加餐 | 王争:如何学习《设计模式之美》专栏?
免费
设计模式之美
登录|注册

41 | 单例模式(上):为什么说支持懒加载的双重检测不比饿汉式更优?

王争 2020-02-05
从今天开始,我们正式进入到设计模式的学习。我们知道,经典的设计模式有 23 种。其中,常用的并不是很多。据我的工作经验来看,常用的可能都不到一半。如果随便抓一个程序员,让他说一说最熟悉的 3 种设计模式,那其中肯定会包含今天要讲的单例模式。
网上有很多讲解单例模式的文章,但大部分都侧重讲解,如何来实现一个线程安全的单例。我今天也会讲到各种单例的实现方法,但是,这并不是我们专栏学习的重点,我重点还是希望带你搞清楚下面这样几个问题(第一个问题会在今天讲解,后面三个问题放到下一节课中讲解)。
为什么要使用单例?
单例存在哪些问题?
单例与静态类的区别?
有何替代的解决方案?
话不多说,让我们带着这些问题,正式开始今天的学习吧!

为什么要使用单例?

单例设计模式(Singleton Design Pattern)理解起来非常简单。一个类只允许创建一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模式,简称单例模式。
对于单例的概念,我觉得没必要解释太多,你一看就能明白。我们重点看一下,为什么我们需要单例这种设计模式?它能解决哪些问题?接下来我通过两个实战案例来讲解。

实战案例一:处理资源访问冲突

我们先来看第一个例子。在这个例子中,我们自定义实现了一个往文件中打印日志的 Logger 类。具体的代码实现如下所示:
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《设计模式之美》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(23)

  • aof 置顶
    这真的是看过的关于讲单例的最好的文章
    2020-02-05
    12
  • Douglas
    争哥新年好, 有个问题想请教一下,单例的实现中看到过一种实现方式,包括在spring源码中有类似的实现 ,代码如下
     1. public class Singleton {
        private static volatile Singleton instance=null;
        private Singleton() {
        }

        public static Singleton getInstance() {//
            Singleton temp=instance; // 为什么要用局部变量来接收
            if (null == temp) {
                synchronized (Singleton.class) {
                    temp=instance;
                    if (null == temp) {
                        temp=new Singleton();
                        instance=temp;
                    }
                }
            }
            return instance;
        }
    }

    spring源码 如 ReactiveAdapterRegistry。
    JDK 源码 如 AbstractQueuedSynchronizer。
    很多地方 都有用 局部变量 来接收 静态的成员变量, 请问下 这么写有什么性能上的优化点吗?
    jcu 包下面类似的用法太多。想弄明白为什么要这样写
    2. 看jdk 官方的文档(JMM)有说明 指令重排发生的地方有很多 ,编译器,及时编译,CPU在硬件层面的优化,看spring 比较新的代码也使用volatile来修饰,你说的new 关键字和初始化 作为原子操作 可以说一下 大概的jdk版本吗
    2020-02-05
    5
    6
  • 辣么大
    简单的方法:创建一个静态私有的filewritter,多线程或者多个Logger对象共享一个filewritter。
    2020-02-05
    3
  • 黄林晴
    打卡

    看过Eventbus 的源码,写法是典型的双重锁检验方式,但是构造方法是public 的

    看源码解释,这是因为EventBus可能有多条总线,订阅者注册到不同线上的 EventBus,通过不同的实例来发送数据,不同的 EventBus 是相互隔离开的,订阅者都只会收到注册到该线上事件。

    但是按照单例定义他又不属于单例,感觉很有疑问
    2020-02-05
    2
    3
  • 忆水寒
    第一个问题,在我的项目中缓存类的节点设置为单例模式,还有加载全局配置文件的类,也设置为了单例模式。
    第二个问题,我是用消息队列实现的日志收集。
    2020-02-05
    2
  • 🤤🤤
    这篇非常棒,🔨
    2020-02-05
    2
  • 小晏子
    1.JDK中 java.lang.Runtime是单例实现的,该类用于管理应用程序运行期间的各种信息,比如memory和processor信息,所以从该类的用途可以知道该类必须是单例的。
    2. 使用多个文件,每new一个实例就使用一个新文件,这样就没有文件覆盖问题了。
    2020-02-05
    2
  • zs阿帅
    争哥,如果服务是多个实例跑,日志那个单例模式会导致覆盖吗?
    2020-02-06
    1
  • 沈康
    1、java bean大部分都是单例吧,例如我们的service bean,单例也是为了复用类和资源共享吧,但是要注意单例需要无状态,有状态的则要考虑线程安全问题
    2、如果线程安全的话,共享一个类就可以了,依赖注入
    2020-02-06
    1
  • Summer 空城
    王老师这篇讲解的非常棒,赞!
    2020-02-05
    1
  • 天天向上卡索
    之前做WP开发的时候用到的硬件如Camera大多是单例,之前看微软的源码有的单例使用并发字典来实现的
    2020-02-07
  • 东流
    1.在你所熟悉的编程语言的类库中,有哪些类是单例类?又为什么要设计成单例类呢?
    java 中的打印类,System.out,(我猜的);原因则是用起来方便

    在第一个实战案例中,除了我们讲到的类级别锁、分布式锁、并发队列、单例模式等解决方案之外,实际上还有一种非常简单的解决日志互相覆盖问题的方法,你想到了吗?
    2020-02-06
  • 小猴子吹泡泡
    如果要实现序列化反序列化的单例模式,要增加一个readResolve()方法,代码如下:
    protected Object readResolve() throws ObjectStreamException {
        return instance;
    }
    2020-02-05
  • javaadu
    第一个问题:Spring框架中,大部分的组件都是单例的;
    第二个问题:将不同业务的日志拆开,并将Logger对象用static关键词修饰
    2020-02-05
  • Frank
    1. 目前能想到的类库中使用了单例模式的有连接池、java.lang.Runtime#getRuntime,Hibernate的SessionFactory、Configuration,这么设计的的原因是维持全局唯一,还存在这些类的实例可能会占用很多内存,或者实例的初始化过程比较冗长。
    2. 写日志的例子中还可使用JDK1.4 提供的FileLock来解决资源竞争问题,其他更简单的方式想不到。
    2020-02-05
  • 辣么大
    写了篇总结,还讨论了单例模式中序列化和反序列化的问题。https://github.com/gdhucoder/DesignPattern/blob/master/021_SingletonPattern.md
    2020-02-05
  • 松花皮蛋me
    MDC,在日记上下文中记录线程信息以区分日记
    2020-02-05
  • 守拙
    Singleton指仅仅被实例化一次的类. Singleton通常被用来代表本质上唯一的系统组件, 如窗口管理器或文件系统.

    -- <Effective Java> p14

    课堂讨论:

    1. 在你所熟悉的编程语言的类库中,有哪些类是单例类?又为什么要设计成单例类呢?

       Android SDK中的AccessibilityManager应用了懒汉式单例模式.

       

    2. 在第一个实战案例中,除了我们讲到的类级别锁、分布式锁、并发队列、单例模式等解决方案之外,实际上还有一种非常简单的解决日志互相覆盖问题的方法,你想到了吗?

    ​ 将FileWriter的声明用static修饰, 即可解决日志相互覆盖的问题. 原理: 被声明为static的成员变量由类的所有实例所共享, 所以Logger类的所有实例都是通过同一FileWriter写入日志到文件.
    2020-02-05
  • Eden Ma
    iOS中UIApplication类是单例,因为它执行的都是应用级别的操作,设计成单例更安全.
    2020-02-05
  • 丿淡忘
    直接将日志写入到数据库???
    2020-02-05
收起评论
23
返回
顶部