定义

单例模式是一个比较简单的模式,其定义如下:

确保某一个类只有一个实例,而且自行实例化,并向整个系统提供这个实力。

优点:

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实现方式的更多相关文章

  1. 【转】几种Java序列化方式的实现

    0.前言 本文主要对几种常见Java序列化方式进行实现.包括Java原生以流的方法进行的序列化.Json序列化.FastJson序列化.Protobuff序列化. 1.Java原生序列化 Java原生 ...

  2. JavaScript中七种函数调用方式及对应 this 的含义

    this 在 JavaScript 开发中占有相当重要的地位,不过很多人对this这个东西都感觉到琢磨不透.要真正理解JavaScript的函数机制,就非常有必要搞清楚this到底是怎么回事. 函数调 ...

  3. Vue 定义组件模板的七种方式(一般用单文件组件更好)

    在 Vue 中定义一个组件模板,至少有七种不同的方式(或许还有其它我不知道的方式): 字符串 模板字面量 x-template 内联模板 render 函数 JSF 单文件组件 在这篇文章中,我将通过 ...

  4. Java:单例模式的七种写法

    第一种(懒汉,线程不安全): 1 public class Singleton { 2 private static Singleton instance; 3 private Singleton ( ...

  5. Java:单例模式的七种写法(转载)

    第一种(懒汉,线程不安全): package Singleton; /** * @echo 2013-10-10 懒汉 线程不安全 */ public class Singleton1 { priva ...

  6. Java:单例模式的七种写法[转]

    第一种(懒汉,线程不安全):  1 public class Singleton {   2     private static Singleton instance;   3     privat ...

  7. 【JAVA学习】单例模式的七种写法

    尊重版权:http://cantellow.iteye.com/blog/838473 第一种(懒汉.线程不安全): Java代码   public class Singleton { private ...

  8. java设计模式之单例模式(七种方法)

    单例模式:个人认为这个是最简单的一种设计模式,而且也是在我们开发中最常用的一个设计模式. 单例模式的意思就是只有一个实例.单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例.这个 ...

  9. Java设计模式之单例模式(七种写法)

    Java设计模式之单例模式(七种写法) 第一种,懒汉式,lazy初始化,线程不安全,多线程中无法工作: public class Singleton { private static Singleto ...

随机推荐

  1. 浅析I/O模型-select、poll、epoll

    I/O流 概念 (1)c++中将数据的输入输出称之为流(stream),在c++中,流被定义为类,成为流类(stream class),其定义的对象为流对象. (2)文件,套接字(socket),管道 ...

  2. 学了Java才搞懂JMeter测试计划

    本文的内容是对这个界面的东西进行讲解: Name 名字,测试计划总得有个名字. Comments 注释,除了名字,还得让人知道它是干嘛的. User Defined Variables 测试计划是根节 ...

  3. Dorado download注意事项

    uploader/uploader-SNAPSHOT uploader/uploader-RELEASE .....使用需要注意!

  4. FFmpeg开发笔记(九):ffmpeg解码rtsp流并使用SDL同步播放

    前言   ffmpeg播放rtsp网络流和摄像头流.   Demo   使用ffmpeg播放局域网rtsp1080p海康摄像头:延迟0.2s,存在马赛克     使用ffmpeg播放网络rtsp文件流 ...

  5. python的数据处理一

    def load_data(filename): features = [] labels = [] f = open(filename, encoding='utf-8') medical = js ...

  6. vuex和axios的基本操作

    1.在src目录下创建一个api 是用于集中处理axios的相关配置 index.js就是处理axios的文件 具体如何使用axios 还请百度axios 2.URLs.js是存放需要请求的地址的 3 ...

  7. javascript播放带透明通道的mp4动画

    随着互联网的发展,动画效果也在一直更新,从刚开始的flsh动画,cocos骨骼动画,到YY开源的svga动画.最近1年来,带有透明通道的mp4动画被使用的极为广泛,对于app端.github上有开源的 ...

  8. C++ 基础 5:多态

    1 什么是多态 多态按字面的意思就是多种形态.当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态. C++ 多态意味着由继承而产生的相关的不同的类,调用重写函数时,会根据实际的对象类型来执 ...

  9. Flask补充内容

    关键字: 一,过滤器 二,增删改查 一,过滤器 1,概念:过滤器的本质就是函数.有时候我们不仅仅只是需要输出变量的值,我们还需要修改变量的显示,甚至格式化.运算等等,而在模板中是不能直接调用 Pyth ...

  10. CentOS修改镜像源头

    CentOS6改为阿里镜像源: 1. cd /etc/yum.repos.d 2. mv CentOS-Base.repo CenOS-Base.repo.bak 3. wget http://mir ...