## **单例模式的定义与特点**
定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式
目的:为了节省内存资源、保证数据内容的一致性等
特点:
1. 单例类只有一个实例对象
2. 该单例对象必须由单例类自行创建
3. 单例类对外提供一个访问该单例的全局访问点
## **单例创建方式**
1. 饿汉式:类初始化时,会立即加载该对象,线程天生安全,调用效率高,项目中常用的模式。
```
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return singleton;
}
}
```
2. 懒汉式:类初始化时,不会初始化该对象,真正需要使用的时候才会创建该对象
```
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public synchronized static Singleton getInstance() {
if (null == singleton) {
singleton = new Singleton();
}
return singleton;
}
}
```
3. 静态内部方式:结合了懒汉式和饿汉式各自的优点,真正需要对象的时候才会加载,加载类是线程安全的
```
public class Singleton {
private Singleton() {
}
// 静态内部类
private static class SingletonInstance {
private static Singleton singleton = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.singleton;
}
}
```
4. 枚举单例:使用枚举实现单例模式(枚举本身就是单例)
5. 双重检测锁方式:懒汉模式的升级版
懒汉模式中加入synchronized锁,每次获取单例对象时都有读取锁的操作,性能必定大大降低,我们的目的只是想要在第一次创建实例的时候加锁就可以了,所以改造代码
~~~
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static Singleton getInstance() {
if (null == singleton) {
synchronized (Singleton.class) {
if (null == singleton) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
~~~
初始化之后,后续再调用就不会有获取锁的操作,解决了性能的问题。看似很完美了,但是似乎哪里还有不多的地方,问题出在 singleton = new Singleton();这一过程可以分解成一下3步:
```
1.分配内存空间
2.初始化对象
3.将对象指向刚分配的内存空间
```
多线程的情况的处理器会进行重排序,有可能执行顺序变成了1->3->2,那么多线程下的调用结果就变成了
| Thread A | Thread B |
| --- | --- |
| 检查到`singleton`为空 | |
| 获取锁 | |
| 再次检查到`singleton`为空 | |
| 为`singleton`分配内存空间 | |
| 将`singleton`指向内存空间 | |
| | 检查到`singleton`不为空 |
| | 访问`singleton`(此时对象还未完成初始化) |
| 初始化`singleton` | |
最终 Thread B访问到了一个未初始化的对象,于是继续改善代码加入`volatile`关键字防止重排序
~~~
public class Singleton {
private volatile static Singleton singleton;
private Singleton() {
}
public static Singleton getInstance() {
if (null == singleton) {
synchronized (Singleton.class) {
if (null == singleton) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
~~~