单例模式?双重检查锁?

301 阅读3分钟

在以前我只有在学习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.为什么使用双重校验

第一层校验,除首次创建实例时需要进入同步锁里面,后续都不会进行加锁操作,因为已经获取到了实例。如果只加一层校验锁,则每次获取都要加锁,无疑会加大了许多开销。

菜鸟教程中对单例模式有详细讲解