单例模式(Singleton)看了就懂
单例,故名思议,一个只能创建一个实例的类。
单例被广泛应用于Spring的bean(默认)、线程池、数据库连接池、缓存,还有其他一些无状态的类如servlet。
一个没必要多例的类实现了单例可以节约空间(显而易见),节省资源(线程、数据库连接)。
单例模式有这么多好处,那我们来实现它吧,首先想到的是创建一个对象要使用new方法,new方法调用的是类的构造函数,想要不被程序员随意的new对象可以将类的构造函数设为私有,然后再提供一个获取这个类实例的方法,所以就有了下面这个实现。
1、只能正确运行在单线程模式下的单例实现:
public class SingletonSingleThread { private static SingletonSingleThread instance; private SingletonSingleThread(){} public static SingletonSingleThread getInstance(){
if(instance == null){
instance = new SingletonSingleThread();
}
return instance;
}
}
以上代码只能保证在单线程下运行,在多线程环境下可能有多个线程在第8行时判断到instance当前是指向null,然后都去执行第9行的代码。
如果读者了解过volatile修饰变量的作用,可能想到将以上代码修改成如下形式,因为volatile可以保证线程之间变量的“可见性”,就可以保证每个线程在第8行判断的时候都是instance的最新引用了?
public class SingletonSingleThread { private static volatile SingletonSingleThread instance; private SingletonSingleThread(){} public static SingletonSingleThread getInstance(){
if(instance == null){
instance = new SingletonSingleThread();
}
return instance;
}
}
但是第8行和第9行是两行代码,并不是原子操作,完全可能出现线程A执行通过第8行校验,准备执行第9行的时候,另一个线程B来到第8行校验,也通过了校验。
实际上就算是代码中的一行指令也不是原子操作,在编译成.class文件后未必是一行字节码,就算是一行字节码,在解释执行或即时编译执行转化成机器码时也可能对应多条指令,以上结论原理不在本文介绍范围之内。
思考:java里有一个很方便的实现线程安全的synchronized修饰符,加上不就实现线程安全了吗?是的,下面这个实现就是在getInstance方法上简单加上synchronized修饰符。
2、对性能不敏感的多线程安全单例实现:
public class SingletonSlow { private static SingletonSlow instance; private SingletonSlow(){} public synchronized static SingletonSlow getInstance(){
if(instance == null){
instance = new SingletonSlow();
}
return instance;
}
}
如果程序对一个单例实现的getInstance()方法效率不敏感可以使用这种实现方式。好处就是直观,简单。坏处也显而易见,只有当SingletonSlow对象第一次被创建时是需要同步的,之后的调用synchronized都将是额外的负担。
思考:能不能只在第一次调用对象实例需要创建的时候才同步代码,其他时候不同步代码的方法呢?有的。
3、DCL单例模式(双重锁检查) 注意:这种方式只能应用于JDK1.5及以后的版本,JDK1.5解决了volatile无法实现双重锁单例的bug:
public class SingletonDCL { private volatile static SingletonDCL instance; private SingletonDCL(){} public static SingletonDCL getInstance(){
if(instance == null){
synchronized(SingletonDCL.class){
if(instance == null){
instance = new SingletonDCL();
}
}
}
return instance;
}
}
DCL方式虽然也用到了同步保证单例,但是它的效率要远远高于第2种实现方式。首先实例变量instance用volatile修饰,保证第8行拿到的是instance的最新引用,这行判断可以快速逃避instance已经被初始化的情况,当然这行代码还是会出现多个线程都判断为真的情况,第9行的代码保证了10-12行代码只有一个线程能执行,第10行重新判断instance引用为空的意义在于解决以下情况的发生:
①若线程A和B同时通过第8行代码判断,等待进入同步块依次执行,若A先执行,则B获得执行时间时instance已经被线程A初始化了。
②若线程A通过第8行代码判断,进入同步块开始执行11行代码未执行完时,线程B执行到第8行判断通过,这时A同步代码块执行完毕,B虽然接下来不需要等待直接进入同步块,但instance也是已经被初始化过的。
思考:这个写法看上去很难理解,我想到一个实现方式,既然是静态变量类型,直接在变量声明时赋值,或者写一个静态块初始化不就好了,静态类型的变量初始化是伴随着类加载进行的,不也是线程安全的吗?确实。
4、利用类加载器实现的急切加载单例:
public class SingletonEagerly { private static SingletonEagerly instance = new SingletonEagerly(); private SingletonEagerly(){} public static SingletonEagerly getInstance(){
return instance;
}
}
为什么叫急切加载单例呢,因为依赖于java的类加载机制,类被加载的时机未必是在需要使用类的对象时,也许在还不需要这个类的实例的时候类的实例就已经被初始化了,如果这个单例很耗费资源,我们肯定想采用懒加载的方式去实现他。
这种实现还有一个缺陷就是依赖于当前线程的ClassLoader(),如果类被多个ClassLoader加载(比如servlet),那就无法保证单例了。
思考:怎样避免类的实例只有在真正需要它的时候才被初始化呢? 可以用私有静态内部类实现。
5、利用类加载器实现的懒加载单例:
public class SingletonLazyByClassLoader { private SingletonLazyByClassLoader(){} public static SingletonLazyByClassLoader getInstance(){
return SingletonHolder.instance;
} private static class SingletonHolder{
private static final SingletonLazyByClassLoader instance= new SingletonLazyByClassLoader();
}
}
将类的唯一实例放在内部静态类里,这样外部只要不调用getInstance方法就不会有类加载器加载去加载内部静态类,而内部静态类是私有的,所以只有第一次instance方法被调用的时候才会初始化了类的实例变量。
遗憾的是这种实现方式仍然无法避免ClassLoader不一样的问题。
如果实在对getInstance效率无要求,使用方式2实现单例最简单直观;
如果程序运行在Jdk1.5及以上的环境,又不会觉得实现麻烦,强烈推荐使用方式3实现单例,高效、可靠;
如果嫌方式3麻烦或者运行在Jdk1.5以前的版本,则根据是否对懒加载有需求使用方式5或方式4,同时要保证程序中用到的类加载器是AppClassLoader或先代加载器是AppClassLoader(并且遵循双亲委托机制),否则单例会失效。
单例模式(Singleton)看了就懂的更多相关文章
- 抽象工厂(Abstract Factory),工厂方法(Factory Method),单例模式(Singleton Pattern)
在谈工厂之前,先阐述一个观点:那就是在实际程序设计中,为了设计灵活的多态代码,代码中尽量不使用new去实例化一个对象,那么不使用new去实例化对象,剩下可用的方法就可以选择使用工厂方法,原型复制等去实 ...
- 设计模式之单例模式——Singleton
设计模式之单例模式--Singleton 设计意图: 保证类仅有一个实例,并且可以供应用程序全局使用.为了保证这一点,就需要这个类自己创建自己的对象,并且对外有 ...
- 【白话设计模式四】单例模式(Singleton)
转自:https://my.oschina.net/xianggao/blog/616385 0 系列目录 白话设计模式 工厂模式 单例模式 [白话设计模式一]简单工厂模式(Simple Factor ...
- 浅谈设计模式--单例模式(Singleton Pattern)
题外话:好久没写blog,做知识归纳整理了.本来设计模式就是个坑,各种文章也写烂了.不过,不是自己写的东西,缺少点知识的存在感.目前还没做到光看即能记住,得写.所以准备跳入设计模式这个大坑. 开篇先贡 ...
- 设计模式之——单例模式(Singleton)的常见应用场景
单例模式(Singleton)也叫单态模式,是设计模式中最为简单的一种模式,甚至有些模式大师都不称其为模式,称其为一种实现技巧,因为设计模式讲究对象之间的关系的抽象,而单例模式只有自己一个对象,也因此 ...
- 设计模式 单例模式(Singleton) [ 转载2 ]
设计模式 单例模式(Singleton) [ 转载2 ] @author java_my_life 单例模式的结构 单例模式的特点: 单例类只能有一个实例. 单例类必须自己创建自己的唯一实例. 单例类 ...
- 设计模式 单例模式(Singleton) [ 转载 ]
设计模式 单例模式(Singleton) [ 转载 ] 转载请注明出处:http://cantellow.iteye.com/blog/838473 前言 懒汉:调用时才创建对象 饿汉:类初始化时就创 ...
- 设计模式之——单例模式(Singleton)的常见应用场景(转):
单例模式(Singleton)也叫单态模式,是设计模式中最为简单的一种模式,甚至有些模式大师都不称其为模式,称其为一种实现技巧,因为设计模式讲究对象之间的关系的抽象,而单例模式只有自己一个对象,也因此 ...
- JAVA设计模式-单例模式(Singleton)线程安全与效率
一,前言 单例模式详细大家都已经非常熟悉了,在文章单例模式的八种写法比较中,对单例模式的概念以及使用场景都做了很不错的说明.请在阅读本文之前,阅读一下这篇文章,因为本文就是按照这篇文章中的八种单例模式 ...
- (转)单例模式(Singleton)的常见应用场景
转自:http://blog.csdn.net/likika2012/article/details/11483167 单例模式(Singleton)也叫单态模式,是设计模式中最为简单的一种模式,甚至 ...
随机推荐
- JSP是什么?
JSP [1] 简介 > HTML - HTML擅长显示一个静态的网页,但是不能调用Java程序. > Se ...
- Wo Wei Shen Me Hui Zai cnblogs Xie Bo Ke
我为什么会在cnblogs上写博客.. CSDN上我上传过代码被很多网友下载过.CSDN我申请过博客.也写过几篇博客. 开源中国 我上传过代码.代码也被网友下载过.OSChina我申请过博客.也写过几 ...
- Python3使用Print输出带颜色字体
Phton3使用print输出带颜色的彩色字体 实现过程: 终端的字符颜色是用转义序列控制的,是文本模式下的系统显示功能,和具体的语言无关. 转义序列是以ESC开头,即用\03 ...
- org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML.问题思路
15:36:34,549 WARN DefaultListableBeanFactory:1416 - Bean creation exception on FactoryBean type chec ...
- linux shell 之在线文本编辑sed
sed命令 文件编辑 sed是一种文本编辑命令,通过终端读取文件数据到缓冲区,然后通过sed编辑文本,在输出到指定的文件,sed是一种流编辑器,它是文本处理中非常中的工具,能够完美的配合正则表达式使用 ...
- Python学习笔记开篇
已经快30岁了,中专学历,不会什么技术,因为好吃懒做最喜欢的就是吃肉睡觉. 每次想学习技术如PhotoShop,绘声绘影,PHP,易语言,按键精灵都只是3分钟热血. 今天我想在业余时间学习Python ...
- Eclipse常用快捷键和调试方法
原文链接:http://my.oschina.net/u/1054538/blog/741561 常用快捷键 Eclipse最全快捷键,熟悉快捷键可以帮助开发事半功倍,节省更多的时间来用于做有意义的事 ...
- photoshop软件应用手记
------------------------常用图片格式------------------------ 位图和矢量图 位图也叫点阵图,是由一个个的方形的像素点排列在一起拼接而成的,位图在放大时, ...
- 一起来学linux:shell script(一)关于变量
p { margin-bottom: 0.25cm; line-height: 120% } (一)首先来看什么是变量,在shell中打印出变量采用的是echo $path或者echo ${path} ...
- 用CAS方案解决高并发一致性问题
详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt395 缘起:在高并发的分布式环境下,对于数据的查询与修改容易引发一致性问题, ...