惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tor Project blog
B
Blog RSS Feed
M
MIT News - Artificial intelligence
WordPress大学
WordPress大学
H
Hackread – Cybersecurity News, Data Breaches, AI and More
罗磊的独立博客
GbyAI
GbyAI
N
Netflix TechBlog - Medium
博客园 - 司徒正美
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
宝玉的分享
宝玉的分享
W
WeLiveSecurity
Stack Overflow Blog
Stack Overflow Blog
Y
Y Combinator Blog
SecWiki News
SecWiki News
V
Vulnerabilities – Threatpost
Google DeepMind News
Google DeepMind News
C
CERT Recently Published Vulnerability Notes
T
Tailwind CSS Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Register - Security
The Register - Security
Cisco Talos Blog
Cisco Talos Blog
Martin Fowler
Martin Fowler
A
About on SuperTechFans
S
Security @ Cisco Blogs
T
Tenable Blog
C
Check Point Blog
N
News and Events Feed by Topic
S
SegmentFault 最新的问题
The GitHub Blog
The GitHub Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
Attack and Defense Labs
Attack and Defense Labs
美团技术团队
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
C
Cisco Blogs
P
Palo Alto Networks Blog
V
V2EX
博客园 - 聂微东
Project Zero
Project Zero
酷 壳 – CoolShell
酷 壳 – CoolShell
D
Docker
N
News | PayPal Newsroom
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
小众软件
小众软件
Application and Cybersecurity Blog
Application and Cybersecurity Blog
人人都是产品经理
人人都是产品经理
V2EX - 技术
V2EX - 技术
I
Intezer
L
LINUX DO - 最新话题

咸糖 - 自律者自由

2025 年终总结:降噪、重构与长期主义 在新加坡和新山吃过最好的食物(持续更新) 写给还在迷茫的你:我的三本大学回忆 2024 年终总结 Neovim: No Crash Incremental Selection 2022 年终总结 使用 neovim 作为 PDE(个性化开发环境) shell 是一个不错的生产力工具 使用二八法则省力地学习 awk 肉身翻墙新加坡安顿指南 使用 Docker Compose 建立你自己的开发环境 关于编写可维护的代码的一些实践与想法 我为什么使用双向链接做笔记? 关于焦虑和拖延症 Golang: 如何处理日渐膨胀的 interface 使用番茄工作法来更好的利用你的时间 Unix 如何杀死一个进程和它的子孙进程? Golang: 让你的零值更有用 使用 Mock 和 Interface 进行 Golang 单测 关于 Golang Slice 的一些细节 总结一些计算机常用的原则 重新学习英语语法 上班族近期小半年入门投资基金组合的学习与实践经历 疫情期间的肉身翻墙新加坡指南 About me 软技能:大厂底层员工打工指南 软技能:我是如何获取知识与信息的? 分布式的令牌桶算法的实现 实现一个AtomicInteger GC root 在哪里? 什么是 Minor GC/Major GC 漏桶算法的设计与实现 剑指offer TCP 针对面试学习 Actor 如何处理阻塞消息 Akka 源码解析 How to learn scala AES 需要限制 SEED 长度 Java 如何区分==与.equals()方法 2018年年度总结 Java 集合扩容
单例模式
xiantang · 2020-04-03 · via 咸糖 - 自律者自由

【注意】最后更新于 April 3, 2020,文中内容可能已过时,请谨慎使用。

单例模式是什么?

单例模式,也叫单子模式,是一种常用的软件设计模式,属于创建型模式的一种。

在这个设计模式中,单例对象的类必须保证只有一个实例存在。

优缺点

优点: 内存中只有一个实例,减少内存开销。

缺点: 违背单一职责原则,并且没有接口,不能继承。

单例模式怎么写

单线程

首先我们从单线程的单例模式开始实现:

先厘清一波思路

单例的构造器应该为 private ,采用 getInstance 来获取对应的实例。

单例对象存在于类属性中,保证只有一个。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Singleton {
    private static Singleton instance = null;
    private Singleton() {
    }

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

单线程的单例比较容易实现,但是如果存在多线程竞争,这个单例模式就可能会造成重复创建。

多线程

我们采用的是双重检验的方式来确保多线程下的访问:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class TwiceCheckSingleton {
    private static TwiceCheckSingleton instance = null;
    private static final Object sybObj = new Object();

    private TwiceCheckSingleton() {
    }

    static TwiceCheckSingleton getInstance() {
        if (instance == null) { // check 1
            synchronized (sybObj) {
                if (instance == null) { // check 2
                    instance = new TwiceCheckSingleton();
                }
            }
        }

        return instance;
    }
}

但是为什么要使用 TwiceCheckSingleton 并且为啥有两次检验呢?

首先我们来分析一下 check 1

其实如果你将check 1 这段代码逻辑删除,和没有删除的效果是一样的,但是为什么要这样做呢?

主要的目的是提高性能,因为如果所有的线程需要获取已经存在的单例都需要进入这个临界区的话,可能会造成性能的下降,使用这个 if 可以将获取的请求直接返回对应的对象。

再来分析一下 check 2

这个操作主要是防止重复创建对象。可以模仿一下这样的场景,当线程并发的进入第一个check (因为实例还没有创建)

1
2
3
4
5
6
7
if (instance == null) { // check 1
  // 并发进入
  // 3个线程
  synchronized (sybObj) {
      instance = new TwiceCheckSingleton();
  }
}

假设有3个线程进入了check1 语句块,一个线程已经进入了check2 并且创建完对象,剩下三个线程会依次进入临界区,并且创建三次对象。

文章作者 xiantang

上次更新 2020-04-03

赞赏支持

微信打赏 支付宝打赏