设计模式一: 单例模式(Singleton)
简介
单例模式是属于创建型模式的一种(另外两种分别是结构型模式,行为型模式).是设计模式中最为简单的一种.
英文单词Singleton的数学含义是"有且仅有一个元素的集合".
从实现层面看, 由类自身管理自己的唯一对象,这个类提供了访问该对象的方式,可以直接访问,不需要实例化(使用new).
动机
设计模式中的Singleton的目的是使类的对象在应用程序中保持唯一,这在某些应用场合非常重要,比如文件系统的资源管理器,又比如应用的日志,应用程序配置等. 保持唯一实例有利于节约系统资源, 同时提升了应用程序性能,避免对资源的多重占用.
实现
一. 简单实现(线程不安全)-懒汉模式
public class Singleton{
private static Singleton onlyOneInstance;
private Singleton(){
}
public static Singleton getOnlyOneInstance(){
if(onlyOneInstance == null){
onlyOneInstance = new Singleton();
}
return onlyOneInstance;
}
}
如果有多个线程同时运行到if(onlyOneInstance == null),并且此时实例为null,那多个线程会执行onlyOneInstance = new Singleton();语句,导致实例化多次.
二. 简单实现(线程安全)-懒汉模式
public static synchronized Singleton getOnlyOneInstance(){
if(onlyOneInstance == null){
onlyOneInstance = new Singleton();
}
return onlyOneInstance;
}
在方法getOnlyOneInstance()上增加同步修饰符synchronized,这样可以保证同一时间只有一个线程访问方法,从而确保不会发生多次实例化.
问题是多个线程访问该方法时会发生阻塞,导致其他线程会在该方法上等待,因此性能上有损耗.
三. 最简单实现(线程安全)-饿汉模式
private static Singleton onlyOneInstance = new Singleton();
这个实现是线程安全的, 但同时也没有延迟加载带来的节约资源的好处.
四. 双重校验锁(线程安全)
public class Singleton{
private volatile static Singleton onlyOneInstance;
private Singleton(){
}
public static Singleton getOnlyOneInstance(){
if(onlyOneInstance == null){
synchronized(Singleton.class){
if(onlyOneInstance == null){
onlyOneInstance = new Singleton();
}
}
}
return onlyOneInstance;
}
}
与第二点的代码比较,本方法将同步锁放在了方法内部,这样可以减少部分线程阻塞,因为在第一个判断if(onlyOneInstance == null)时如果实例不为null,方法就返回了.
与第二点的代码比较,多个线程依然可能会同时进入到if(onlyOneInstance == null),同样的,此时onlyOneInstance有可能是null,因此需要在synchronized(Singleton.class)内部再次判断if(onlyOneInstance == null)
onlyOneInstance 使用 volatile 修饰的必须的.解释这点的原因需要深入到 JVM 的指令重排机制.onlyOneInstance = new Singleton();的执行需要分三步:
1. 分配内存
2. 初始化对象
3. 将 onlyOneInstance 指向分配的内存地址
由于 JVM 的指令重排特性,上述三步步骤可能会重排为 1>3>2 ,在多线程环境下访问onlyOneInstance时, 有可能会得到一个未被初始化的实例.而valatile关键字可以阻止 JVM 的指令重排,从而保证多线程环境下正常运行.
五. 静态内部类实现
public class Singleton{
private Singleton(){}
private static class SingletonHolder{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getUniqueInstance(){
return SingletonHolder.INSTANCE;
}
}
这种实现利用了 JVM 保持线程安全性, 同时也具备了延迟加载的好处.
六. 枚举实现
public enum Singleton{
INSTANCE
}
单例的最佳实践, 一则实现简单, 二则面对复杂的序列化或反射攻击的时候能够防止实例化多次.
反射攻击:可以使用反射原理, 通过setAccessible()方法提升构造函数的访问级别为public,然后通过new实例化对象.
对于序列化和反序列化,因为每一个枚举类型和枚举变量在JVM中都是唯一的,即Java在序列化和反序列化枚举时做了特殊的规定,枚举的writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法是被编译器禁用的,因此也不存在实现序列化接口后调用readObject会破坏单例的问题。
枚举为什么是最佳实践?
首先, 枚举是class实现的,也就是说它可以有成员变量及成员函数,这是我们可以使用它来实现单例的基础. 另外,枚举继承了Enum类,它不能作为子类继承其他类,但可以实现接口, 它也不能被其他类继承, 枚举编译后的类会添加final修饰符, 编译后的代码如下:
public final class EnumClass extends Enum{
}
其次,枚举有且仅有private构造器,这点满足单例模式的条件.而枚举值的初始化是在静态代码中进行.枚举实现的单例模式不具备懒加载的作用.
设计模式一: 单例模式(Singleton)的更多相关文章
- 设计模式之单例模式——Singleton
设计模式之单例模式--Singleton 设计意图: 保证类仅有一个实例,并且可以供应用程序全局使用.为了保证这一点,就需要这个类自己创建自己的对象,并且对外有 ...
- 设计模式(4) -- 单例模式(Singleton)
设计模式(4) -- 单例模式(Singleton) 试想一个读取配置文件的需求,创建完读取类后通过New一个类的实例来读取配置文件的内容,在系统运行期间,系统中会存在很多个该类的实例对象,也就是说 ...
- 乐在其中设计模式(C#) - 单例模式(Singleton Pattern)
原文:乐在其中设计模式(C#) - 单例模式(Singleton Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 单例模式(Singleton Pattern) 作者:weba ...
- 【设计模式】单例模式-Singleton
[设计模式]单例模式-SingletonEnsure a class has only one instance, and provide a global point to access of it ...
- 设计模式之——单例模式(Singleton)的常见应用场景
单例模式(Singleton)也叫单态模式,是设计模式中最为简单的一种模式,甚至有些模式大师都不称其为模式,称其为一种实现技巧,因为设计模式讲究对象之间的关系的抽象,而单例模式只有自己一个对象,也因此 ...
- 设计模式之单例模式(Singleton Pattern)
单例模式 单例模式(Singleton Pattern)在java中算是最常用的设计模式之一,主要用于控制控制类实例的数量,防止外部实例化或者修改.单例模式在某些场景下可以提高系统运行效率.实现中的主 ...
- 设计模式之——单例模式(Singleton)的常见应用场景(转):
单例模式(Singleton)也叫单态模式,是设计模式中最为简单的一种模式,甚至有些模式大师都不称其为模式,称其为一种实现技巧,因为设计模式讲究对象之间的关系的抽象,而单例模式只有自己一个对象,也因此 ...
- java设计模式之 单例模式 Singleton
static 的应用 单例模式 Singleton 单例:保证一个类在系统中最多只创建一个实例. 好处:由于过多创建对象实例,会产生过多的系统垃圾,需要GC频繁回收,由于GC会占用较大的系统资源,所有 ...
- 【设计模式】单例模式 Singleton Pattern
通常我们在写程序的时候会碰到一个类只允许在整个系统中只存在一个实例(Instance) 的情况, 比如说我们想做一计数器,统计某些接口调用的次数,通常我们的数据库连接也是只期望有一个实例.Windo ...
随机推荐
- LightGBM大战XGBoost,谁将夺得桂冠?
引 言 如果你是一个机器学习社区的活跃成员,你一定知道 提升机器(Boosting Machine)以及它们的能力.提升机器从AdaBoost发展到目前最流行的XGBoost.XGBoost实际上已经 ...
- Linux使用百度云
导读 百度云没有Linux客户端,于是有大神用Go语言写出来一个叫BaiduPCS-Go的命令行盘客户端,可以通过终端操作百度云盘,在Linux上实现上传下载.但是因为是命令行版本的,对没有命令行使用 ...
- Spring Cloud微服务Ribbon负载均衡/Zuul网关使用
客户端负载均衡,当服务节点出现问题时进行调节或是在正常情况下进行 服务调度.所谓的负载均衡,就是当服务提供的数量和调用方对服务进行 取舍的调节问题,在spring cloud中是通过Ribbon来解决 ...
- Java 常用数据结构对象的实现原理 集合类 List Set Map 哪些线程安全 (美团面试题目)
Java中的集合包括三大类,它们是Set.List和Map, 它们都处于java.util包中,Set.List和Map都是接口,它们有各自的实现类. List.Set都继承自Collection接口 ...
- [模板] dp套dp && bzoj5336: [TJOI2018]party
Description Problem 5336. -- [TJOI2018]party Solution 神奇的dp套dp... 考虑lcs的转移方程: \[ lcs[i][j]=\begin{ca ...
- JarvisOJ Misc shell流量分析
分析一下shell流量,得到flag 看着一大推的数据记录头都大了,并没有什么wireshark的使用经验,开始胡搞 首先用notepad++打开,搜索flag字样找到了一个类似于python脚本的东 ...
- python之内置函数(二)与匿名函数、递归函数初识
一.内置函数(二)1.和数据结构相关(24)列表和元祖(2)list:将一个可迭代对象转化成列表(如果是字典,默认将key作为列表的元素).tuple:将一个可迭代对象转化成元组(如果是字典,默认将k ...
- 从备份文件bak中识别SQL Server的版本
SQLServer 的备份文件是以.bak 为后缀的文件,如果想要通过备份文件查看数据库版本,通常的做法就是把数据库还原,但是在还原的过程,如果不是相同的数据库版本,就会导致无法还原: 在数据库中,低 ...
- BZOJ 4030: [HEOI2015]小L的白日梦
4030: [HEOI2015]小L的白日梦 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 172 Solved: 39[Submit][Statu ...
- 小米平板2 win10 MIUI互刷教程
在这篇文章中,我们会为大家提供Windows 10版小米平板2刷入MIUI和MIUI版小米平板2刷入Windows 10的两组教程. 不过从Win 10刷MIUI需要用原生安卓系统过渡来统一bios版 ...