设计模式专题-02-单例模式
个人github地址:HibisciDai
设计模式系列项目源码:HibisciDai/DesignPattern-LearningNotes-HibisciDai
processon在线UML类图:processon
[TOC]
设计模式-02-单例模式
单例模式(Singleton Pattern)
意图
一个类仅有一个实例,并提供一个访问它的全局访问点。
注意
- 该类不能被外界任意实例化(构造函数私有化)。
- 该类向外界提供一个可以获得该类实例的方法。
- 该类只能被实例化一次。
主要解决
一个全局使用的类频繁的创建与销毁。
关键代码
构造函数是私有的。
应用实例
一个党只有一个主席。
优点
- 在内存中只有一个实例,减少内存开销。
- 避免资源的多重占用。
缺点
没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外边如何实例化。
其它
getInstance()
方法中需要使用同步锁 synchronized(Singleton.class)
防止多线程同时进入造成 instance
被多次实例化。
实现
1 | public class SingleObject { |
单例模式的几种实现方式
懒汉式,线程不安全
- 是否Lazy初始化
是
- 是否多线程安全
否
- 描述
最基本的实现方式,不支持多线程和线程安全,严格来说并不是多线程。
- 代码实例
1 | public class Singleton { |
懒汉式,线程安全
- 是否Lazy初始化
是
- 是否多线程安全
是
- 描述
可在多线程中很好的工作,但是效率低,必须加锁 synchronized
才能保证单例,但加锁会影响效率。getInstance()
的性能对应用程序不是很关键
- 代码实例
1 | public class Singleton { |
饿汉式
- 是否Lazy初始化
否
- 是否多线程安全
是
- 描述
常用方式,但容易产生垃圾对象。基于classloade机制避免了多线程的同步问题,instance在类装载的时候就实例化。
- 优点
没有加锁,执行效率会提高。
- 缺点
类加载时就初始化,浪费内存。
- 代码实例
1 | public class Singleton { |
双检锁/双重校验锁(DCL,即double-checked locking)
jdk1.5之后支持
- 是否Lazy初始化
是
- 是否多线程安全
是
- 描述
这种方式采用双锁机制,安全且在多线程情况下能保持高性能。 getInstance()
的性能对应用程序很关键。
- 代码实例
1 | public class Singleton { |
登记式/静态内部类
- 是否Lazy初始化
是
- 是否多线程安全
是
- 描述
可以达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
该方式利用 classloader 机制保证初始化 instance 时只有一个线程
与第三种方式不同的是:第三种方式只要 Singleton 类被装载了,那么 instance 就会被实例化;
该方式 Singleton 类被装载了,instance不一定被初始化。
因为 SingletonHolder 类没有被主动使用,只有显式调用 getInstance 方法时,才会显示装载 SingletonHolder 类,从而实例化 instacne。
- 代码实例
1 | public class Singleton6 { |
枚举
jdk1.5之后
- 是否Lazy初始化
否
- 是否线程安全
是
- 描述
这种实现方式还没有被广泛采用,是最佳方法,避免多线程,支持序列化,绝对防止多次实例化。
不能通过 reflection attack 来调私有构造方法。
- 代码实例
1
2
3
4
5publc enum Singleton {
INSTANCE;
public void whateverMethod() {}
}
关于一些经验
不建议1 2方式,建议3
明确实现 lazy loading 时候,使用5
涉及序列化,用6
其它特殊需求,考虑4