单例模式

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

类型: 创建类模式

类图:

单例模式特点

1、单例类只能有一个实例。

2、单例类必须自己创建自己的唯一实例。

3、单例类必须给所有其他对象提供这一实例。

单例模式应该是23种设计模式中最简单的一种模式了。它有以下几个要素:

  • 私有的构造方法
  • 指向自己实例的私有静态引用
  • 以自己实例为返回值的静态的公有的方法

单例模式的实现方式

单例模式根据实例化对象时机的不同分为两种:

一种是饿汉式单例,一种是懒汉式单例。

饿汉式单例在单例类被加载时候,就实例化一个对象交给自己的引用;

懒汉式在调用取得实例方法的时候才会实例化对象。

1)饿汉式单例

是否 Lazy 初始化:否
是否多线程安全:是
实现难度:易
描述:这种方式比较常用,但容易产生垃圾对象。
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。
/**
* 饿汉式单例
* <p>
* 饿汉式是典型的空间换时间,当类装载的时候就会创建类的实例,
* 不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断,节省了运行时间。
*
* @author kaifeng
*/
public class HungrySingleton {
private static HungrySingleton hungrySingleton = new HungrySingleton(); /**
* 私有构造函数
*/
private HungrySingleton() {
} /**
* 获取实例对象
*/
public static HungrySingleton getInstance() {
return hungrySingleton;
}
}

2)懒汉式单例

是否 Lazy 初始化:是
是否多线程安全:是
实现难度:易
描述:这种单例是典型的时间换空间,只有使用的时候才会实例化,但是每次获取实例的时候都会进行判断,是否已经实例,花费了一些判断时间,如果一直没有使用就不会实例化,节省了内存空间
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
/**
* 懒汉式单例
* <p>
* 这种单例是典型的时间换空间,只有使用的时候才会实例化,但是每次获取实例的时候都会进行判断,
* 是否已经实例,花费了一些判断时间,如果一直没有使用就不会实例化,节省了内存空间
*
* @author kaifeng
*/
public class LazySingleton {
private static LazySingleton instance = null; /**
* 私有构造函数
*/
private LazySingleton() {
} /**
* 静态工厂方法,实例化对象
*/
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}

3)双检锁/双重校验锁(DCL,即 double-checked locking)

是否 Lazy 初始化:是
是否多线程安全:是
实现难度:较复杂
描述:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
/**
* 双检锁/双重校验锁(DCL,即 double-checked locking)
*
* @author kaifeng
*/
public class DCLSingleton { private volatile static DCLSingleton instance; private DCLSingleton() {
} public static DCLSingleton getInstance() {
//第一次检查实例是否存在,如果不存在才进入下面的同步块,否则直接返回实例
if (instance == null) {
//同步代码块,线程安全的创建实例
synchronized (DCLSingleton.class) {
//第二次检查实例是否存在,如果不存在才真正的创建实例
if (instance == null) {
instance = new DCLSingleton();
}
}
}
return instance;
}
}

所谓“双重检查加锁”机制:并不是每次进入getInstance方法都需要同步,而是先检查实例是否存在,如果不存在才进行下面的同步块,这是第一重检查,进入同步块过后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了,从而减少了多次在同步情况下进行判断所浪费的时间。

“双重检查加锁”机制的实现会使用关键字volatile,被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。

4)Initialization-on-demand holder idiom

是否 Lazy 初始化:是
是否多线程安全:是
实现难度:一般
描述:这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
/**
* 在这个类级内部类里面去创建对象实例,只要不使用到这个类级内部类,
* 那就不会创建对象实例,从而同时实现延迟加载和线程安全。
*
* @author kaifeng
*/
public class Singleton {
private Singleton() {
} /**
* 类级的内部类,就是静态的成员式内部类,该内部类的实例与外部类的实例
* 没有绑定关系,而且只有被调用时才会装载,从而实现了延迟加载。
*/
private static class SingletonHolder {
/**
* 静态初始化器,由JVM来保证线程安全
*/
private static Singleton instance = new Singleton();
} public static Singleton getInstance() {
return SingletonHolder.instance;
}
}

当getInstance方法第一次被调用的时候,第一次读取SingletonHolder.instance,导致SingletonHolder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,因此只会在虚拟机装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。

这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。

5)枚举

是否 Lazy 初始化:否
是否多线程安全:是
实现难度:易
描述:这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。
不能通过 reflection attack 来调用私有构造方法。
/**
* @author kaifeng
*/
public enum EnumSingleton {
/**
* 定义一个枚举的元素,它就代表了Singleton的一个实例
*/
instance; /**
* 单例可以有自己的操作
*/
public void singletonCustom() {
//自定义操作
}
}

单例模式的优点:

  • 在内存中只有一个对象,节省内存空间。
  • 避免频繁的创建销毁对象,可以提高性能。
  • 避免对共享资源的多重占用。
  • 可以全局访问。

适用场景:

  • 需要频繁实例化然后销毁的对象。
  • 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
  • 有状态的工具类对象。
  • 频繁访问数据库或文件的对象。
  • 系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器

单例模式注意事项:

  • 只能使用单例类提供的方法得到单例对象,不要使用反射,否则将会实例化一个新对象。
  • 不要做断开单例类对象与类中静态引用的危险操作。
  • 多线程使用单例使用共享资源时,注意线程安全问题。

【java设计模式】-04单例模式的更多相关文章

  1. java 设计模式之单例模式

    -------Success is getting what you want, happiness is wanting what you get. java设计模式之单例模式(Singleton) ...

  2. 折腾Java设计模式之单例模式

    博文原址:折腾Java设计模式之单例模式 单例模式 Ensure a class has only one instance, and provide a global point of access ...

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

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

  4. Java 设计模式之单例模式(一)

    原文地址:Java 设计模式之单例模式(一) 博客地址:http://www.extlight.com 一.背景 没有太多原由,纯粹是记录和总结自己从业以来经历和学习的点点滴滴. 本篇内容为 Java ...

  5. java设计模式1——单例模式

    java设计模式1--单例模式 1.单例模式介绍 1.1.核心作用:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点 1.2.常见场景 1.3.单例模式的优点 1.4.常见的五种单例模式实现 ...

  6. java设计模式之单例模式你真的会了吗?(懒汉式篇)

    java设计模式之单例模式你真的会了吗?(懒汉式篇) 一.什么是单例模式? 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一.这种类型的设计模式属于创建型模式,它提供 ...

  7. java设计模式之单例模式(几种写法及比较)

    概念: Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建 ...

  8. java设计模式- (1)单例模式

    参加校园招聘的笔试,发现公司都会考一些java设计模式,所以上网查询相关内容,总结常用的几种单例模式. 单例模式(Singleton Pattern)是 Java中最简单的设计模式之一.这种类型的设计 ...

  9. [转]JAVA设计模式之单例模式

    原文地址:http://blog.csdn.net/jason0539/article/details/23297037 概念: java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主 ...

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

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

随机推荐

  1. mysql replace substring 字符串截取处理

    SELECT a1,a2,replace(a2, "豫ICP备16006180号-", "") a22,a3,a4,a5 FROM `aaab` order b ...

  2. USB相关资料汇总

    [1]USB规范,一切的一切,基本的基本,天书级别USB_11_spec(中文).pdf    USB1.1规范(中文版) usb_20.pdf                     USB2.0规 ...

  3. 客户端相关知识学习(九)之h5给app传递数据

    方法一: 情况一: if (window.JdAndroid){          window.JdAndroid.setPayCompleted();          window.JdAndr ...

  4. Spring web.xml详解

    web.xml文件是Java Web项目中的一个配置文件,主要用于配置欢迎页.Filter.Listener.Servlet等,但并不是必须的,一个Java Web项目没有web.xml文件也是照样能 ...

  5. 9.SpringMVC注解式开发-处理器的请求映射规则的定义

    1.对请求URI的命名空间的定义 @RequestMapping的value属性用于定义所匹配请求的URI.但对于注解在方法上和注解在类上, 其value 属性 所指定的URI,意义是不同的 一个@C ...

  6. Spring Data JPA 大纲归纳

    第一天: springdatajpa day1:orm思想和hibernate以及jpa的概述和jpa的基本操作 day2:springdatajpa的运行原理以及基本操作 day3:多表操作,复杂查 ...

  7. linux入门常用指令4.挂载数据盘

    挂载硬盘 #查看当前分区情况 [root@localhost ~]# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sdb 8:16 0 5G 0 dis ...

  8. 学习elasticsearch(一)linux环境搭建(1)

    首先安装了Oracle Virtual Box 然后安装了最小版的CentOS.由于vbox自带的操作面板不太好用,于是用了xshell,XShell连接最小版的centOS时遇到的问题记录下. 1. ...

  9. Linux之df磁盘信息

    df命令用于查看磁盘的分区,磁盘已使用的空间,剩余的空间 1.用法 df [选项] [文件..] 2.命令选项 -a,--all 全部文件系统-h,--human-readable 以以合适的单位来显 ...

  10. python : import详解。

    用户输入input() input()函数: 用于从标准输入读取数值. >>> message = input('tell me :') tell me :hahah >> ...