单例模式与它的七种java实现方式
定义
单例模式是一个比较简单的模式,其定义如下:
确保某一个类只有一个实例,而且自行实例化,并向整个系统提供这个实力。
优点:
1.由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,且创建或销毁时性能又无法优化,单例模式的优势就非常明显。
2.由于单例模式只生成一个实例,所以减少了系统的性能开销,当一个对象的产生需要比较多的资源时,如读取配置,产生其他的依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后用永久驻留内存的方式解决。(在javaEE中采用单例模式时需要注意jvm的垃圾回收机制)
3.单例模式可以避免对资源的多重占用,例如一个写文件的动作,由于只有一个实例存在在内存中,可以避免堆同一个资源文件的同时写操作
4.单例模式可以在系统设置全局的访问点,优化和共享资源访问。例如可以设计及一个类,负责所有数据包的映射处理。
缺点:
1.单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现。单例模式为什么不能增加接口呢?因为接口是对单例模式没有任何意义的,特殊情况下单例也可能实现接口或者被继承。
2.单例堆测试是不利的。在并行开发环境中,如果单例模式没有完成,是不能进行测试的。没有接口也不能使用mock方式虚拟一个对象。
3.单例模式与单一职责原则有冲突。一个类应该只实现一个逻辑,而不关心它是否是单例的,是不是单例取决于环境,单例模式把要单例和业务模式融合在一个类中。
单例模式的使用场景:
1.要求生成唯一序列号的环境
2.在整个项目中需要一个共享访问点或共享数据,例如一个web页面上的计数器,可以使用单例模式保持计数器的值,并确保是线程安全的
3.创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源。
4.需要定义大量的静态常量和静态方法的环境,可以采用单例模式。
另外,在Spring中,每个bean默认就是单例的。这样做的优点是,spring容器可以管理这些Bean的生命周期,决定什么时候创建出来,什么时候销毁,销毁的时候要如何处理等等。如果采用非单例模式,则Bean初始化后的滚啊里交由J2EE容器,Spring容器就不再跟踪管理Bean的生命周期了。
注意:所有的单例模式都是使用静态方法进行创建的,所以单例对象在内存中静态共享区中存储。
Java实现单例模式
第一种(懒汉,线程不安全):
public class SingletonDemo1 {
private static SingletonDemo1 instance;
private SingletonDemo1(){}
public static SingletonDemo1 getInstance(){
if (instance == null) {
instance = new SingletonDemo1();
}
return instance;
}
}
这种写法lazy loading很明显,但是致命的是在多线程不能正常工作。
第二种(懒汉,线程安全):
public class SingletonDemo2 {
private static SingletonDemo2 instance;
private SingletonDemo2(){}
public static synchronized SingletonDemo2 getInstance(){
if (instance == null) {
instance = new SingletonDemo2();
}
return instance;
}
}
这种写法在getInstance()方法中加入了synchronized锁。能够在多线程中很好的工作,而且看起来它也具备很好的lazy loading,但是效率很低(因为锁),并且大多数情况下不需要同步。
第三种(饿汉):
public class SingletonDemo3 {
private static SingletonDemo3 instance = new SingletonDemo3();
private SingletonDemo3(){}
public static SingletonDemo3 getInstance(){
return instance;
}
}
这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,这时候初始化instance显然没有达到lazy loading的效果。
第四种(饿汉,变种):
public class SingletonDemo4 {
private static SingletonDemo4 instance = null;
static{
instance = new SingletonDemo4();
}
private SingletonDemo4(){}
public static SingletonDemo4 getInstance(){
return instance;
}
}
表面上看起来差别挺大,其实更第三种方式差不多,都是在类初始化即实例化instance
第五种(静态内部类):
public class SingletonDemo5 {
private static class SingletonHolder{
private static final SingletonDemo5 instance = new SingletonDemo5();
}
private SingletonDemo5(){}
public static final SingletonDemo5 getInsatance(){
return SingletonHolder.instance;
}
}
这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,它跟第三种和第四种方式不同的是(很细微的差别):第三种和第四种方式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比第三和第四种方法就显得更合理。
第六种(枚举):
public enum SingletonDemo6 {
instance;
public void whateverMethod(){
}
}
这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊,不过,个人认为由于1.5中才加入enum特性,用这种方式写不免让人感觉生疏,在实际工作中,我也很少看见有人这么写过。
第七种(双重校验锁):
public class SingletonDemo7 {
private volatile static SingletonDemo7 singletonDemo7;
private SingletonDemo7(){}
public static SingletonDemo7 getSingletonDemo7(){
if (singletonDemo7 == null) {
synchronized (SingletonDemo7.class) {
if (singletonDemo7 == null) {
singletonDemo7 = new SingletonDemo7();
}
}
}
return singletonDemo7;
}
}
这些实现的共同之处是全部私有化成员变量和构造函数,并且有一个公开的getInstance函数。注意,在第七种双重检查中,成员变量必须加volatile关键字修饰。否则很可能出现一个线程创建了一半的对象被另一个线程访问,出现空指针异常。
参考文献:
https://www.cnblogs.com/Ycheng/p/7169381.html
设计模式之禅 秦小波 著
单例模式与它的七种java实现方式的更多相关文章
- 【转】几种Java序列化方式的实现
0.前言 本文主要对几种常见Java序列化方式进行实现.包括Java原生以流的方法进行的序列化.Json序列化.FastJson序列化.Protobuff序列化. 1.Java原生序列化 Java原生 ...
- JavaScript中七种函数调用方式及对应 this 的含义
this 在 JavaScript 开发中占有相当重要的地位,不过很多人对this这个东西都感觉到琢磨不透.要真正理解JavaScript的函数机制,就非常有必要搞清楚this到底是怎么回事. 函数调 ...
- Vue 定义组件模板的七种方式(一般用单文件组件更好)
在 Vue 中定义一个组件模板,至少有七种不同的方式(或许还有其它我不知道的方式): 字符串 模板字面量 x-template 内联模板 render 函数 JSF 单文件组件 在这篇文章中,我将通过 ...
- Java:单例模式的七种写法
第一种(懒汉,线程不安全): 1 public class Singleton { 2 private static Singleton instance; 3 private Singleton ( ...
- Java:单例模式的七种写法(转载)
第一种(懒汉,线程不安全): package Singleton; /** * @echo 2013-10-10 懒汉 线程不安全 */ public class Singleton1 { priva ...
- Java:单例模式的七种写法[转]
第一种(懒汉,线程不安全): 1 public class Singleton { 2 private static Singleton instance; 3 privat ...
- 【JAVA学习】单例模式的七种写法
尊重版权:http://cantellow.iteye.com/blog/838473 第一种(懒汉.线程不安全): Java代码 public class Singleton { private ...
- java设计模式之单例模式(七种方法)
单例模式:个人认为这个是最简单的一种设计模式,而且也是在我们开发中最常用的一个设计模式. 单例模式的意思就是只有一个实例.单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例.这个 ...
- Java设计模式之单例模式(七种写法)
Java设计模式之单例模式(七种写法) 第一种,懒汉式,lazy初始化,线程不安全,多线程中无法工作: public class Singleton { private static Singleto ...
随机推荐
- pycharm配置django rest framework
安装django rest framework pip install 添加rest_framework app 在settings.py INSTALLED_APPS = [ 'django.co ...
- model的基础操作
1.创建表 1.1 Meta源信息 from django.db import models class UserInfo(models.Model): username = models.CharF ...
- 1. Spark Word Count
1. request: 2. scala: sc.textFile("input").flatMap(_.split(" ")).map((_,1)).redu ...
- 如何做可靠的分布式锁,Redlock真的可行么
本文是对 Martin Kleppmann 的文章 How to do distributed locking 部分内容的翻译和总结,上次写 Redlock 的原因就是看到了 Martin 的这篇文章 ...
- 【应用程序见解 Application Insights】Application Insights 使用 Application Maps 构建请求链路视图
Applicaotn Insigths 使用 Application Maps 构建请求链路视图 构建系统时,请求的逻辑操作大多数情况下都需要在不同的服务,或接口中完成整个请求链路.一个请求可以经历 ...
- filter 请求过程中过滤链组装和调用解析
自定义filter需要先下载依赖包 首先过滤链不是责任链的设计模式,因为一个request可以被链条上的多个filter处理.但是,对于责任链来说,一个request只能被链条中的一个handler处 ...
- 老师问学生while判断
老师问学生,这道题你会做了吗?如果学生答"会了(y)",则可以放学.如果学生不会做(n),则老师再讲一遍,再问学生是否会做了...... (1)直到学生会为止,才可以放学. (2) ...
- 关于 node build/build.js 卡顿的问题
今天也是奇了怪了,npm run dev 可以正常执行,但node build/build.js 就死活不行,卡住不动 实在不行,就有了"重启"万能大法,结果依然不行.实在无奈, ...
- Flink基础:时间和水印
往期推荐: Flink基础:入门介绍 Flink基础:DataStream API Flink基础:实时处理管道与ETL Flink深入浅出:资源管理 Flink深入浅出:部署模式 Flink深入 ...
- 斯福赛特:中了.Devos勒索病毒。所有文件被加密了,如何解密解决?
什么是.devos勒索病毒? 也称为DHARMA勒索软件1,它通过加密文件并要求支付赎金以恢复对文件的访问来修改您的文件. DHARMA勒索软件通过名为.devos的新加密病毒再次活跃.该特定的病毒家 ...