锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

DCL单例到底需不需要volatile?

时间:2023-06-29 03:37:00 dcl光伏电抗器三相功率电感器

DCL需要单例吗?volatile?

    • 介绍前言知识
      • 对象的创建过程
      • 半初始化
    • DCL(Double Check Lock)
      • volatile关键字
      • 指令重排序
    • 最后

介绍前言知识

首先,让我们来看看下面的程序。这个程序很简单。这个程序的运行结果是什么?8还是0?

public class Escape { 
             private int num = 8;      private Escape() { 
                     new Thread(() -> System.out.println(this.num)).start();     }      public static void main(String[] args) throws IOException { 
                 new Escape();         System.in.read();     } } 

对象的创建过程

java在程序运行过程中,每时每刻都有对象被创建,在语言层面上,创建对象只是一个new只是关键词,字节码层面的对象创建过程是什么?

 LINENUMBER 21 L0     NEW lixinzhen/Escape     DUP     INVOKESPECIAL lixinzhen/Escape.<init> ()V     POP 

这是前面程序反编译的字节码。由此可见,创建对象需要经过三个步骤。第一步是申请内存,给对象默认值0(此时已成为半初始化状态)。第二步是调用构造方法初始化。对象赋值为8,第三步是建立连接(执行到INVOKESPECIAL这里的对象已经创建)。(DUP这个指令涉及到堆栈的调用,这里不介绍太复杂了)

半初始化

在创建对象的过程中,作者提到了半初始状态。你有没有想过为什么要有半初始状态,也就是说,为什么要给它?num对象赋予初始值0?原因在于给num对象分配的内存区域以前可能有一定的值,以防止num该值被错误分配,因此首先给出初始值。(这也是为什么Java比C/C 更安全的原因,早期c/c 指针暴露在外面,可以随时窃取密码)

DCL(Double Check Lock)

在设计模式中,最简单的是单例模式,那么什么是单例模式呢?我相信我们都知道,所谓的单例模式是在整个类别的运行过程中,只有一个对象的例子。作者还介绍了单例模式,如果你不太了解,你可以先看看这篇文章。饥饿模式和懒惰模式

看完这篇文章,你会发现在单例模式下保证线程安全的最佳方案应该是DCL,在此,笔者复制其代码进行后续分析。

public class SingletonLazy { 
        
    private static SingletonLazy singleton;
    private SingletonLazy() { 
        
    }
    public static SingletonLazy getInstance() { 
        
        //再次判断当前singleton是否已经创建,如果创建,直接获取
        if (singleton == null) { 
        
            synchronized (SingletonLazy.class) { 
        
                //先判断当前对象是否已经创建
                if (singleton == null) { 
        
                    singleton = new SingletonLazy();
                }
            }
        }
        return singleton;
    }
}
public class SingletonTest { 
        
    public static void main(String[] args) { 
        
        SingletonLazy instance = SingletonLazy.getInstance();
        SingletonLazy instance2 = SingletonLazy.getInstance();
        System.out.println(instance==instance2);//true
    }
}

在这里笔者想提一个问题,最上面的那个if语句能不能去掉?

我们知道加是一个非常耗资源耗性能的事情,如果说加锁需要100ns,那么if语句只需要1ns。如果有1W个线程同时进来,此时去掉了最上面的if语句的话,那么就需要对这个1W个线程进行校验也就是说需要对这1W个线程全部加锁,那么造成的性能损耗是非常严重的。而加了if语句后,可以方便的对这个1W个线程进行判断。

volatile关键字

Java并发编程有3大特性,即可见性,有序性,原子性。如果你看过《深入理解Java虚拟机》这本书你就应该知道在Java内存模型中始终存在乱序问题,因此可见性和原子性可以保证,但有序性却很难保证。

voltalie关键字大家应该比较熟悉,它有两方面的作用:保证可见性和禁止重排序。

那么它是如何做到的呢?其实归根到底就是Lock 指令。(这是汇编指令,这里不细讲,后续笔者会对缓存一致性协议进行相关文章编写的时候会讲到)

指令重排序

指令重排序(处理器的乱序执行)是指源码顺序和程序顺序不一样,指令重排序不是必然发生的,指令重排序会导致线程安全问题。(也就是说单线程结果的最终一致性没有问题,可以随便换)

在单线程环境下指令重排序没有影响,但在多线程环境下指令重排序很有可能会乱序执行。前面讲到的volatile关键字可以避免指令重排序。

最后

人生有三大境界:看山是山,看山不是山,看山还是山。笔者能力有限,只能写出这么多,如有不当之处还请多指教。

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章