Java双重校验锁单例原理

目录
  • 前言
  • 正文
    • 代码实现
  • 总结

前言

作为开发者,单例这个就再也熟悉不过了,但是作为多种单例实现模式,我个人觉得双重校验锁是非常不多的实现,我们简单来分析一下其原理。

正文

先来说一下Java版本的,后面会涉及Kotlin中的代码我们再做比对。

代码实现

Java代码实现如下:

//双重校验锁单例
public class SingleInstance {
    //必须volatile修饰 见分析1
    private volatile static SingleInstance instance;
    //私有化构造函数
    private SingleInstance() {
    }

    public static SingleInstance getInstance() {
        //第一个判空 见分析2
        if (instance == null) {
            synchronized (SingleInstance.class) {
                //第二个判空 见分析3
                if (instance == null) {
                    //新建实例
                    instance = new SingleInstance();
                }
            }
        }
        return instance;
    }
}

首先这里synchronized关键字没有修饰整个getInstance函数,因为这个函数可能使用地方很多,这样就会造成其他线程阻塞,不太好,所以这里只同步了一段代码。

分析2:为什么在进入同步代码块时需要进行进行判空,假如有线程A和线程B,这时线程A先判断instance为null,所以它进入了同步代码块,创建了对象,然后线程B再进来时,它就不必再进入同步代码快了,可以直接返回,也其实也就是懒加载,可以加快执行速度。

分析3:为什么在同步代码块中还要再进行一次判断呢,假如有线程A和线程B,它俩A先调用方法,B紧接着调用,这时A、B在分析2出的判空都是空,所以A进入同步代码块,B进行等待,当A进入同步代码块中创建了对象后,A线程释放了锁,这时B再进入,如果这时不加分析3的判空,B又会创建一个实例,这明显不符合规矩。

分析1:那既然加了2层判断,那为什么还要加个volatile关键字呢,这里知识点就有点多了。

因为新建实例的代码:

instance = new SingleInstance();

它不是一个原子操作,这个简单的赋值可以分为3步:

1、给SingleInstance分配内存

2、调用SingleInstance的构造方法

3、把instance指向分配的内存空间

这是正常逻辑的3个步骤,也只有按1 2 3执行后,这个instance才不是null。

但是Java内存模型允许这个进行指令重排序,也就是这3步可能是123也可能是132,所以这里就有问题了。

假如线程A和线程B,线程A已经跑到分析3处的代码,这时这条指令执行是132,刚把步骤3执行完,这时线程B跑到了分析1处的代码,会发现instance不为null了,这时线程B就直接返回了,从而导致错误。

既然知道了原因,那volatile关键字就是解决这个的,它可以禁止指令重新排序,而且保证所有线程看到这个变量是一致的,也就是不会从缓存中读取(这个特性后面有机会再说),所以在创建instance实例时,它的步骤都是123,就不会出错了。

总结

到此这篇关于Java双重校验锁单例原理的文章就介绍到这了,更多相关Java校验锁单例内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java 单例模式详细解释

    目录 饿汉式 懒汉式 懒汉式(加锁synchronized) 懒汉式(部分加锁synchronized) 懒汉式(DCL) 懒汉式(DCL)最终版 静态内部类 总结 饿汉式 /** * 饿汉式 * 类加载到内存后,就是实例化一个单例,JVM保证线程安全 * 简单使用:推荐使用 * 唯一缺点:不管用与不用,类加载时就会完成实例化 */ public class Demo01 { //开始先新建一个对象 private static final Demo01 INSTANCE = new Demo0

  • Java双重检查加锁单例模式的详解

    什么是DCL DCL(Double-checked locking)被设计成支持延迟加载,当一个对象直到真正需要时才实例化: class SomeClass { private Resource resource = null; public Resource getResource() { if (resource == null) resource = new Resource(); return resource; } } 为什么需要推迟初始化?可能创建对象是一个昂贵的操作,有时在已知的运

  • Java 实例解析单例模式

    目录 单例模式的介绍 优点 缺点 Synchronized Synchronized示例 Synchronized与非Synchronized Singleton 第一个示例 第二个示例 第三个示例 第四个示例 第五个示例 单例模式的介绍 单例对象(Singleton)是一种常用的设计模式.在实际使用中,单例对象能保证在一个JVM中,该对象只存在一个实例存在. 优点 1.减少系统开销,提高系统性能 2.省去了new操作符,降低了系统内存的使用频率,减轻GC压力 3.避免对共享资源的多重占用 缺点

  • Java单例模式的6种实现方式详解

    目录 为什么使用单例模式 使用单例模式需要注意的关键点 单例模式的几种写法 1. 饿汉式 2. 懒汉式 3. DCL(Double CheckLock)实现单例 4. 静态内部类 5. 枚举单例 6. 容器实现单例 总结 为什么使用单例模式 需要确保某个类只要一个对象,或创建一个类需要消耗的资源过多,如访问IO和数据库操作等,这时就需要考虑使用单例模式了. 使用单例模式需要注意的关键点 将构造函数访问修饰符设置为private 通过一个静态方法或者枚举返回单例类对象 确保单例类的对象有且只有

  • Java双重校验锁单例原理

    目录 前言 正文 代码实现 总结 前言 作为开发者,单例这个就再也熟悉不过了,但是作为多种单例实现模式,我个人觉得双重校验锁是非常不多的实现,我们简单来分析一下其原理. 正文 先来说一下Java版本的,后面会涉及Kotlin中的代码我们再做比对. 代码实现 Java代码实现如下: //双重校验锁单例 public class SingleInstance { //必须volatile修饰 见分析1 private volatile static SingleInstance instance;

  • java 中设计模式之单例

    java 中设计模式之单例 设计模式思想 什么是设计模式:我作为初学者,今天第一次正式学习设计模式,我觉得对与理解什么是设计模式很重要,那么什么是设计模式呢? 设计模式:解决问题的一种行之有效的思想. 设计模式:用于解决特定环境下.重复出现的特定问题的解决方案 我的理解是前人在软件设计的时候碰到了一类问题,他们总结出了一套行之有效,并且经过验证的解决方案. 设计模式的优点: 1.设计模式都是一些相对优秀的解决方案,很多问题都是典型的.有代表性的问题,学习设计模式,我们就不用自己从头来解决这些问题

  • 浅谈Java编程中的单例设计模式

    写软件的时候经常需要用到打印日志功能,可以帮助你调试和定位问题,项目上线后还可以帮助你分析数据.但是Java原生带有的System.out.println()方法却很少在真正的项目开发中使用,甚至像findbugs等代码检查工具还会认为使用System.out.println()是一个bug. 为什么作为Java新手神器的System.out.println(),到了真正项目开发当中会被唾弃呢?其实只要细细分析,你就会发现它的很多弊端.比如不可控制,所有的日志都会在项目上线后照常打印,从而降低运

  • Java正确实现一个单例设计模式的示例

    设计模式中的单例,是最常用,也算是比较简单的一个了.我们都知道,要想保证只有一个实例,通常采用加锁和双重检查的方式来实现单例,代码如下. public class SingletonTest { private SingletonTest(){ } private static SingletonTest instance; public static SingletonTest getInstance(){ if(instance == null){ synchronized (Singlet

  • Java synchronized偏向锁的核心原理详解

    目录 1.偏向锁的核心原理 2.偏向锁的撤销 3.偏向锁的膨胀 4.偏向锁的好处 总结 1. 偏向锁的核心原理 轻量级锁在没有竞争时(就自己这个线程),每次重入仍然需要执行 CAS 操作. Java 6 中引入了偏向锁来做进一步优化:只有第一次使用 CAS 将线程 ID 设置到对象的 Mark Word 头,之后发现 这个线程 ID 是自己的就表示没有竞争,不用重新 CAS.以后只要不发生竞争,这个对象就归该线程所有. public class Main { static final Objec

  • 详解JAVA 七种创建单例的方法

    1 饿汉式 public class Singleton1 { //不能延迟加载 占用内存 耗费资源 private static Singleton1 singleton1 = new Singleton1(); public static Singleton1 getSingleton1() { return singleton1; } } 可以保证多个线程下唯一实例,getSingleton1 方法性能较高,但是无法进行懒加载. 2 懒汉式 public class Singleton2

  • Java synchronized轻量级锁的核心原理详解

    目录 1.轻量级锁的原理 2.轻量级锁的分类 1.普通自旋锁 2.自适应自旋锁 3.轻量级锁的膨胀 总结 问题: 什么是自旋锁? 说一下 synchronized 底层实现原理? 多线程中 synchronized 锁升级的原理是什么? 1. 轻量级锁的原理 引入轻量级锁的主要目的是在多线程竞争不激烈的情况下,通过CAS机制竞争锁减少重量级锁产生的性能损耗.重量级锁使用了操作系统底层的互斥锁(Mutex Lock),会导致线程在用户态和核心态之间频繁切换,从而带来较大的性能损耗. 轻量级锁的使用

  • Java ynchronized重量级锁的核心原理详解

    目录 1.monitor原理 2.snychronized同步代码块原理 3.synchronized同步方法原理 4.重量级锁的开销 总结 在JVM中,每个对象都关联一个监视器,这里的对象包含Object实例和Class实例.监视器是一个同步工具,相当于一个许可证,拿到许可证的线程即可进入临界区进行操作,没有拿到则需要阻塞等待.重量级锁通过监视器的方式保障了任何时间只允许一个线程通过受到监视器保护的临界区代码. 1. monitor原理 jvm中每个对象都会有一个监视器Monitor,监视器和

  • Java中双重检查锁(double checked locking)的正确实现

    目录 前言 加锁 双重检查锁 错误的双重检查锁 隐患 正确的双重检查锁 总结 前言 在实现单例模式时,如果未考虑多线程的情况,就容易写出下面的错误代码: public class Singleton { private static Singleton uniqueSingleton; private Singleton() { } public Singleton getInstance() { if (null == uniqueSingleton) { uniqueSingleton =

  • C#单例类的实现方法

    单例类保证一个类全局仅有一个实例,并提供一个全局访问点,由于只能生成一个实例,因此我们必须把构造函数设为私有函数以禁止他人创建实例. 实现1:懒汉式,线程不安全 该实现没有额外开销,不要求线程安全的情况下可以使用: public class Singleton1 { private static Singleton1 instance = null; private Singleton1() { } public static Singleton1 Instance { get { if (in

随机推荐