单例模式(Singleton Pattern): 单例模式确保某一个类只有一个实例, 而且自行实例化并向整个系统提供这个实例, 这个类称为单例类, 它提供全局访问的方法.
设计模式-概述
很多情况只需要一个对象就可以满足要求, 例如Factory类型的对象, 使用单例模式能让系统避免创建大量对象, 减少资源占用开销.
结构
单例模式仅有一个角色 : Singleton, 并且私有化其构造器, 提供一个方法返回实例, 实例的创建时机由本类控制.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @startuml sigleton-1 !include ./common.cuml
Class(Singleton,"单例类") { - instance : Singleton + getInstance() : Singleton + singletonOperation() : void - Singleton() : void }
note AS A if (instance == NULL) instance = new Singleton(); return instance; end note
A .left. Singleton
@enduml
|
使用时序
可以分为两种情况
使用时再创建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @startuml sigleton-2
actor 用户 as U participant Singletion as S
U -> S : getInstance() : Singleton activate U
alt 已经创建过实例 U <-- S : 返回已有创建的示例 else 未创建过实例 S -> S : 创建实例 U <-- S : 返回刚刚创建的示例 deactivate U end
@enduml
|
预先创建
1 2 3 4 5 6 7 8 9 10 11 12 13
| @startuml sigleton-3
actor 用户 as U participant Singletion as S
S -> S : 创建实例 ... 等待需要 ... U -> S : getInstance() : Singleton activate U U <-- S : 返回已有创建的示例 deactivate U
@enduml
|
代码实现
大家经常把”使用时再创建对象”称为 懒汉式
, “预先创建对象”称为饿汉式
.
懒汉式
懒汉式由于是使用时再创建, 如果多线程环境, 多个地方同时调用 getInstance()
, 则很可能出现并发问题, 导致创建了多个对象, 所以要对其特殊处理, 防止出现并发问题.
使用于单线程环境
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Singleton {
private volatile static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() { if (null == instance) { instance = new Singleton(); } return instance; } }
|
使用于多线程环境, 效率不高
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Singleton {
private volatile static Singleton instance = null;
private Singleton() {}
public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
|
双重检验锁(推荐)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class Singleton {
private volatile static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
|
静态内部类方式(推荐)
1 2 3 4 5 6 7 8 9 10 11 12
| public class Singleton { private Singleton() {}
public static Singleton getInstance() { return SingletonHolder.INSTANCE; }
private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } }
|
饿汉式
类初始化时就创建了实例
基于私有静态对象(推荐)
1 2 3 4 5 6 7 8 9 10 11
| public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() { }
public static Singleton getInstance() { return instance; } }
|
基于枚举(推荐)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public interface iSingleton { void doSomething(); }
public enum Singleton implements iSingleton { INSTANCE { @Override public void doSomething() { System.out.println("complete singleton"); } };
public static iSingleton getInstance() { return Singleton.INSTANCE; } }
|
使用场景
- 一个系统仅仅需要一个实例对象, 例如发号器, 工厂, 数据源等等
- 客户端仅需要通过一个公开的访问源来获取实例.
具体应用
Spring管理的Bean对象, 很多都是单例的, 例如一个系统可能仅需要一个HttpClient, 对同一个数据库实例的连接仅需要一个DataSource, 各种中间件单个实例或集群的连接工厂类都仅需要一个xxxConnectionFactory, Redis操作管理仅仅需要一个RedisService, 或者是一个系统内的编号生成器, 这些都仅仅需要一个实例就够用了.