设计模式-单例模式

单例模式(Singleton Pattern): 单例模式确保某一个类只有一个实例, 而且自行实例化并向整个系统提供这个实例, 这个类称为单例类, 它提供全局访问的方法.

设计模式-概述

很多情况只需要一个对象就可以满足要求, 例如Factory类型的对象, 使用单例模式能让系统避免创建大量对象, 减少资源占用开销.

结构

单例模式仅有一个角色 : Singleton, 并且私有化其构造器, 提供一个方法返回实例, 实例的创建时机由本类控制.

sigleton-1.png

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

使用时序

可以分为两种情况

  • 使用时再创建对象
  • 预先创建对象

使用时再创建

sigleton-2.png

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

预先创建

sigleton-3.png

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, 或者是一个系统内的编号生成器, 这些都仅仅需要一个实例就够用了.