1. 什么是单例模式?
保证一个类只有一个实例:线程池、缓存、日志对象,spring中的bean默认单例。
核心:一个私有静态变量、一个私有构造函数、一个公有静态函数。
2. 实现方式
2.1 懒汉式实现
通过私有静态变量被延迟实例化。
优点:如果没有用到该类,那么就不会进行实例化,节约资源。
多线程下不安全:
Pulic class Singleton{
Private static Singleton Instance = null;
Private Singleton(){
}
Public static Single getInstance(){
If(Instance == null){
Instance = new Singleton();
}
Return Instance;
}
}
实现线程安全:
Public static Synchronized Single getInstance(){
If(Instance == null){
Instance = new Singleton();
}
Return Instance;
}
2.2 饿汉式实现
直接实例化,不会产生线程不安全问题,但是失去了延迟实例化的好处。
实现:
Private static Singleton Instance = new Singleton();
2.3 双重校验锁实现
先判断是否实例化,如果没有实例化,才对实例化语句进行加锁。
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;
}
}
Instance采用volatile修饰的必要性,Instance = new Singleton();这句代码分为三步执行:
- 初始化Instance
- 为Instance分配空间
- 将Instance指向分配的空间
重排后可能变为231,设置了Instance指向分配好的内存空间,但是还没有初始化对象,加入volatile关键字可以禁止重排序。
由于JVM具有指令重排的特性,在多线程下会导致一个线程获得还没有初始化的实例。volatile关键字可以禁止重排,保证多线程下正常运行。
volatile关键字:
volatile修饰的变量会在读写操作前后插入内存屏障,可以禁止指令进行重排。
可以保证有序性、可见性。
但是不能保证原子性。
2.4 静态内部类实现
当Singleton类被加载时,静态内部类SingletonHolder没有被加载进内存,只有当调用getInstance方法从而触发SingletonHolder.Instance时SIngleton才会被加载,此时初始化Instance实例,并且JVM能确保Instance只被实例 化一次。
优点:
具有延时初始化的好处,还保证了线程安全。
Public class Singleton{
Private Singleton(){
}
Private static class SIngletonHolder{
Private static final Singleton Instance = new Singleton();
}
Public static Singleton getInstance(){
Return SingletonHolder.Instance;
}
}