在以前我只有在学习java基础的时候学习过单例模式!现在还清晰记得当时是在B站看宋红康老师的JAVA基础讲解!当时还以为单例模式只有懒汉式与饿汉式!没有深入的进行了解!最近接触到了双重检查锁之后就重新在网上找资料!全面学习了单例模式!下面是我对单例模式的一知半解!
单例模式实现方式与简介
因为菜鸟上面对于这六种有详细的解释,而对双重检查锁可能写的不是太深入!于是在下面我对双重检查锁进行了重新的介绍一下。
- 懒汉式(不同步)
- 懒汉式(同步)
- 饿汉式
- 双重检查锁
- 静态内部类
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
- 枚举
双重检查锁
此作者比我更加的厉害!分析的更加到位!如果需要更细致的学习,可以点下面链接: 非常优秀的双重检查锁流程分析
双重检查锁代码实现:
public class SingletonTest {
// 使用volatile保证可见性
private static volatile SingletonTest singletonTest = null;
private SingletonTest() {
}
public static SingletonTest getInstance() {
//第一重校验
if (singletonTest == null) {
// 加锁
synchronized (SingletonTest.class) {
// 第二重校验
if (singletonTest == null) {
singletonTest = new SingletonTest();
}
}
}
return singletonTest;
}
}
首先当一个线程发出请求后,会先检查instance是否为null,如果不是则直接返回其内容,这样避免了进入synchronized块所需要花费的资源。其次,即使第2节提到的情况发生了,两个线程同时进入了第一个if判断,那么他们也必须按照顺序执行synchronized块中的代码,第一个进入代码块的线程会创建一个新的Singleton实例,而后续的线程则因为无法通过if判断,而不会创建多余的实例。
双重检查锁定背后的理论是完美的。不幸地是,现实完全不同。双重检查锁定的问题是:并不能保证它会在单处理器或多处理器计算机上顺利运行。
双重检查锁定失败的问题并不归咎于 JVM 中的实现 bug,而是归咎于 Java 平台内存模型。内存模型允许所谓的“无序写入”,这也是这些习语失败的一个主要原因。于是加Volatile关键字解决无序写入问题!
两个关键点理解:
1.使用volatile保证可见性
volatile功能:当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值
因为JVM虚拟机在执行代码时,底层会优化不会按照我们写的代码顺序执行。
2.为什么使用双重校验
第一层校验,除首次创建实例时需要进入同步锁里面,后续都不会进行加锁操作,因为已经获取到了实例。如果只加一层校验锁,则每次获取都要加锁,无疑会加大了许多开销。