Java设计模式——单例模式(创建型模式)
概述
单例模式保证对于每一个类加载器,一个类仅有一个实例并且提供全局的访问。其是一种对象创建型模式。对于单例模式主要适用以下几个场景:
- 系统只需要一个实例对象,如提供一个唯一的序列号生成器
- 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例
单例模式的缺点之一是在分布式环境中,如果因为单例模式而产生 bugs,那么很难通过调试找出问题所在,因为在单个类加载器下进行调试,并不会出现问题。
实现方式
一般来说,实现有五种方式:饿汉式、懒汉式、双重锁检验、静态内部类、枚举,而这里我将这五种方式分为三部分来介绍。
饿汉式加载
public final class Singleton {
//私有构造器,所以无法实例化类对象
private Singleton() {}
//类静态实例域
private static final Singleton INSTANCE = new Singleton();
//返回类实例
public static Singleton getInstance() {
return INSTANCE;
}
}
直接初始化静态实例保证了线程安全,但是此种方式不是懒加载的,单例一开始就初始化了,无法在我们需要的时候再进行初始化。
懒汉式加载
//实例在这个方法第一次被调用的时候进行初始化
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
getInstance()方法设置为synchronized保证了线程安全,但是其效率并不高,因为在任何时候只有一个线程能够访问这个方法,而同步操作仅需在第一次被调用的时候才被需要。
此方法的一种改进是使用双重检验锁。
public final class ThreadSafeDoubleCheckLocking {
private static volatile ThreadSafeDoubleCheckLocking instance;
private ThreadSafeDoubleCheckLocking() {}
public static ThreadSafeDoubleCheckLocking getInstance() {
//局部变量可以提高25%的性能,这个局部变量确保instance只在已经被初始化的情况下读取一次
//《Effective Java 第2版》P250页
ThreadSafeDoubleCheckLocking result = instance;
//检查实例是否已经别初始化
if (result == null) {
//未被初始化,但是无法确定这时其他线程是否已经对其初始化,因此添加对象锁进行互斥
synchronized (ThreadSafeDoubleCheckLocking.class) {
//再一次将instance赋值给局部变量来进行检查,因为有可能在当前线程阻塞的时候,其他线程对instance进行初始化
result = instance;
if (result == null) {
//此时还未被初始化的话,在这里初始化可以保证线程安全
instance = result = new ThreadSafeDoubleCheckLocking();
}
}
}
return result;
}
}
上面的双重检验锁使用了《Effective Java 第2版》提出的一个优化方式,另外值得一提的是,对于instance域被声明为volatile是很重要的。当一个变量定义为volatile之后,它就具备了两种特性,第一是保证了此变量对所有线程的可见性,“可见性”指的是当一条线程修改了这个变量的值,新值对于其他线程来说是可以立即得知的(注意基于volatile变量的运算在并发编程下并非是安全的,例如:假设被volatile修饰的域进行自增运算,而自增运算并不是原子操作,那么第二个线程就可能在读取旧值和写回新值的期间读取到这个域,导致第二个线程看到的值与第一个线程未自增前的值一样,详细了解的话可查看《深入理解Java虚拟机 第2版》P366 基于volatile型变量的特殊规则);第二是禁止指令重排序优化。
在进行初始化的时候instance = result = new ThreadSafeDoubleCheckLocking(),此时 JVM 大致做了三件事:
- 1.给instance分配内存
- 2.调用构造函数进行初始化
- 3.instance对象指向被分配的内存
没有声明为volatile,那么指令重排序后,可能执行的顺序是 1-3-2,当线程一执行到3这个步骤,还未执行步骤2(instance非null,但未初始化),那么对于线程二,此时检测到 instance 并非是 null,直接返回 instance,就会出现错误。需要说明的一点是,JDK 1.5以后,volatile才真正发挥用处,因此在1.5以前,仍然是无法保证安全的,具体可查看 The "Double-Checked Locking is Broken" Declaration.
另外一种懒加载方式就是使用静态内部类的方法:
public final class InitializingOnDemandHolderIdiom {
private InitializingOnDemandHolderIdiom() {}
public static InitializingOnDemandHolderIdiom getInstance() {
return HelperHolder.INSTANCE;
}
private static class HelperHolder {
private static final InitializingOnDemandHolderIdiom INSTANCE =
new InitializingOnDemandHolderIdiom();
}
}
这种方式是线程安全的,同时也是懒加载的。HelperHolder是私有的,除了getInstance()外没有办法访问。这种方式不需要依赖其他语言特性(volatile,synchronized),也不依赖JDK版本。
枚举
《Effective Java 第2版》P15 中提到实现单例的一种新方式,使用枚举来实现单例。枚举类型是Java 5中新增特性的一部分,因此使用这种方式实现的枚举,要求至少是 JDK 1.5版本及其以上。枚举本身保证了线程安全,并且提供了序列化机制,因此这种方式写起来极为简洁。
public enum Singleton {
INSTANCE;
}
当然,对于使用枚举来实现单例模式也有一些缺点,具体可以查看 StackExchange 的讨论。
典型使用场景
- 日志纪录类
- 管理与数据库的连接
- 文件管理系统
具体实例
java.lang.Runtime#getRuntime()
java.awt.Desktop#getDesktop()
java.lang.System#getSecurityManager()
参考资料
- java-design-patterns
- 《Effective Java 第2版》
Java设计模式——单例模式(创建型模式)的更多相关文章
- Java设计模式之创建型模式
创建型模式分为五类:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式 一.工厂方法模式:接口-实现类.工厂类
- Java设计模式_创建型模式_单例模式
单例模式的实现: 定义一个类,在类中定义该类的静态变量,再定一个一个获取该类的静态变量的方法. UML图:
- [C#]设计模式-单例模式-创建型模式
单例模式用于在整个软件系统当中保持唯一实例,在 C# 当中最能够体现此概念的就是静态类,静态类的生命周期是跟随整个程序,并且在整个程序中仅保有一个实例. 不过在这里我们不再详细阐述单例模式与静态类有什 ...
- Java设计模式 - - 单例模式 装饰者模式
Java设计模式 单例模式 装饰者模式 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 静态代理模式:https://www.cnblogs.com/StanleyBlogs/p/1 ...
- 设计模式(Java版)-创建型模式之简单工厂模式
前言:这段时间在学习设计模式,本人也是小菜一枚(所以写的如果有错误的地方请大大们给予指出).这个东西也是我一直想学习的,从点点滴滴做起,记录下自己每天的领悟! 一.工厂模式的动机 在软件系统中,经常面 ...
- GoF的23种设计模式之创建型模式的特点和分类
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”.这样可以降低系统的耦合度,使用者不需要关注对象的创建细节,对象的创建由相关的工厂来完成.就像我们去商场购买商品时, ...
- Typescript玩转设计模式 之 创建型模式
作者简介 joey 蚂蚁金服·数据体验技术团队 前言 我们团队的工作是用单页面应用的方式实现web工具.涉及到数万到十数万行的前端代码的管理,而且项目周期长达数年. 怎么样很好地管理好这种量级的前端代 ...
- 单例模式——创建型模式01
1. 名称 单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类.单例模式是一种对象创建型模式. 2. 问题 ...
- 设计模式01 创建型模式 - 单例模式(Singleton Pattern)
参考 [1] 设计模式之:创建型设计模式(6种) | 博客园 [2] 单例模式的八种写法比较 | 博客园 单例模式(Singleton Pattern) 确保一个类有且仅有一个实例,并且为客户提供一 ...
- Java设计模式之职责型模式总结
原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6548127.html 所谓职责型模式,就是采用各种模式来分配各个类的职责. 职责型模式包括 ...
随机推荐
- Mongo--03 mongo副本集、备份与恢复
目录 一.mongo副本集配置 二.查看副本集状态 三.副本集权重调整 四.创建节点 五.仲裁节点 六.mongo备份与恢复 七.准备测试数据 一.mongo副本集配置 1.创建节点目录和数据目录 # ...
- CF1151F Sonya and Informatics (计数dp+矩阵优化)
题目地址 Solution (duyi是我们的红太阳) (这里说一句:这题看上去是一个概率dp,鉴于这题的概率dp写法看上去不好写,我们其实可以写一个计数dp) 首先拿到这个题目我们要能设出一个普通d ...
- CF1260C Infinite Fence 题解(扩欧)
题目地址 CF1260C 题目大意 现有\(10^{100}\)块木板需要涂漆,第x块如果是x是a的倍数,则涂一种颜色,是b的倍数,则涂另一种颜色.如果既是a又是b的倍数,那么两种颜色都可以涂:如果连 ...
- LOJ 6060「2017 山东一轮集训 Day1 / SDWC2018 Day1」Set(线性基,贪心)
LOJ 6060「2017 山东一轮集训 Day1 / SDWC2018 Day1」Set $ solution: $ 这一题的重点在于优先级问题,我们应该先保证总和最大,然后再保证某一个最小.于是我 ...
- 唤醒 App
一.Deep Link 1.什么是 Deep Link? Deep Link 是 App 的深度连接,当单击链接或编程请求调用Web URI意图时,Android系统按顺序依次尝试以下每一个操作,直到 ...
- 2019年8月12日~8月18日 第七周JAVA学习总结
本周主要完成了PTA代码编写的任务,将报告也完成了,但是由于同学聚会比较多所以JAVA的学习比较少,也没有学习统一建模语言.用在编程的时间平均每天5个小时,用在JAVA学习上平均每天1个小时,用在解决 ...
- osi7层模型及线程和进程
端口的作用: 在同一台电脑上,为了让不同 的程序分离开来! http:网站默认端口是80 https:网站默认端口是443 osi七层模型: 1.应用层:软件 2.表示层:接收数据 3.会话:保持登录 ...
- @PathVariable注解使用
@PathVariable是spring3.0的一个新功能:接收请求路径中占位符的值 语法: @PathVariable("xxx")通过 @PathVariable 可以将URL ...
- JSP实现大文件上传和下载
javaweb上传文件 上传文件的jsp中的部分 上传文件同样可以使用form表单向后端发请求,也可以使用 ajax向后端发请求 1.通过form表单向后端发送请求 <form id=" ...
- R 保存图片——tiff
tiff(filename = "c:\\aaa.tiff", res = 800, pointsize = 2) plot(1:100) dev.off() tiff(file= ...