单例模式简单介绍
单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类成为单例类,它提供全局访问的方法。单例模式的要点有三个:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。
单例模式需要注意事项
1.单例类的构造函数私有
2.提供一个自身的静态私有成员变量
3.提供一个公有的静态工厂方法
模式结构图
单例模式实例
这里模拟实现一个居民身份证唯一的单例场景。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24// 单例类如下
public class IdCardNo {
private static IdCardNo instance = null;
private String no;
private IdCardNo() {
}
public static IdCardNo getInstance() {
if (instance == null) {
instance = new IdCardNo();
instance.setNo("No5000011113333");
}
return instance;
}
public String getNo() {
return no;
}
private void setNo(String no) {
this.no = no;
}
单例的多种写法
上述场景中使用的是懒汉式写法,单例模式还有如下的几种写法
饿汉式:1
2
3
4
5
6
7
8
9
10
11public class EagerSingleton {
private static EagerSingleton instance = new EagerSingleton();
private EagerSingleton() {
}
public static EagerSingleton getInstance() {
return instance;
}
}
饿汉式的写法可以保证线程安全,但从资源利用率角度来考虑,比懒汉式写法稍差。但懒汉式存在线程安全问题,所以接下来考虑多个线程同时首次引用单例的访问限制问题。
双重检测的单例:
1 | public class Singleton { |
双重检测虽然解决了多线程的访问限制问题,但这个写法看起来着实不美观。那么我们还有没有别的写法呢?答案是有的。
基于枚举的方式:1
2
3
4
5
6
7
8
9
10
11
12
13public enum SingletonEnum {
INSTANCE;
private Singleton instance = null;
private SingletonEnum() {
instance = new Singleton();
}
public Singleton getInstance() {
return instance;
}
}
单元素的枚举类可以保证单例的线程安全、序列化,除单元素枚举外,还有使用 java
内部类实现的方式。
基于内部类实现单例:
1 | public class Singleton implements Serializable { |
使用静态内部类的优点是:
外部类加载时并不需要立即加载内部类,即当 Singletonle 被加载时,并不需要去加载 SingletonHandler ,只有当 getInstance() 方法第一次被调用时,才会去初始化 SingletonHandler ,同时初始化该类的静态变量 instance ,在确保线程安全的同时也延迟了单例的实例化. |
总结
一个类模板,在整个系统中只允许产生一个实例叫做单例。单例有多种写法:懒汉式、饿汉式、双重检查、枚举、内部类。
- 饿汉式不管用不用先创建出来,保证线程安全。
- 懒汉式延迟加载,有效利用资源不保证线程安全。
- 双重检测方式保证了懒汉式的线程安全问题。
- 单元素枚举可以同时保证线程安全和序列化。
- 内部类使用了
jvm
的类加载机制来保证线程安全和懒加载。 - 序列化和反序列化保证单例需要重写类的
readResolve()
方法