zoukankan      html  css  js  c++  java
  • happens-before 原则 && volatile 在单例模式中的应用

    https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4  

    Happens-before 的定义

    通过上面的定义,我们得出 happens-before 的原则: 

    happens-before 定义了什么时候会发生数据争用(即:多线程读取共享变量)。

    happens-before 原则:

    1. 对锁(对象监视器)的 释放(unlock)操作 happens-before 锁的 lock 操作
    
    2. 对 volatile 字段的写操作 happens-before 对这个字段的读操作
      从底层实现来看,volatile 写操作会通过汇编中的 lock 前缀指令,对这块内存区域的缓存行进行锁定(相当于加入了内存屏障,从而保证程序执行顺序),
    这样,如果此时正在对 volatile 变量时行写操作,那么其他线程所有的读操作都需要等待写操作完成,这就是 volatile 写操作 happens-before 对这个字段读操作的原因
    3. 对线程的 start() 的调用 happens-before 这个线程里面的代码的执行 4. t1.join() 成功后,那么 t1 线程里面的代码的执行 happens-before main 线程 5. 一个对象默认的初始化操作 happens-before 这个程序里面的其他操作

    当一个程序包含两个冲突的访问,且这两个访问没有按照 happens-before 原则进行排序,那么,我们称之为数据争用。

    线程间操作以外的操作的语义,例如,读取数组长度、执行已检查的强制转换,以及调用虚拟方法,不会直接影响数据争用。

    当且仅当所有顺序一致的执行都没有数据争用时,程序才能正确同步。

    如果一个程序正确同步,则该程序的所有执行将看起来是顺序一致的。

    这是对程序员的极其有力的保证。程序员无需考虑重排序(指令重排)即可确定其代码包含数据争用。因此,在确定其代码是否正确同步时,他们无需考虑指令重排。一旦确定代码正确同步,程序员就不必担心指令重排会影响他的代码。

    一个程序必须正确的同步,从而避免在指令重排时得到意想不到的结果。使用正确的同步不能确保程序的整体行为正确。但是,同步的使用让程序员可以以一种简单的方式来推理程序的可能行为。正确同步的程序的行为很少依赖于可能的指令重排。没有正确同步的程序,就可能得到非常奇怪、令人困惑和违反直觉的结果。

    volatile 的应用之——双重校验锁实现单例模式:

    这种方式必须使用 volatile 修饰符,以确保程序的正确性,避免指令重排导致获取到一个未被初始化的对象,从而导致程序崩溃。这里使用到了 volatile 可以保证有序性的特性

    (volatile 是 jdk 1.5 之后出现的)

     1 /**
     2  * 查看字节码
     3  * javap -v -p -s -sysinfo -constants NewTest.class
     4  * javap -v NewTest.class
     5  */
     6 public class NewTest {
     7     public volatile Object o;
     8 
     9     /**
    10      * <pre>
    11      *     public void m();
    12      *        descriptor: ()V
    13      *        flags: ACC_PUBLIC
    14      *        Code:
    15      *        stack=3, locals=1, args_size=1
    16      *        0: aload_0
    17      *        1: new           #2                  // class java/lang/Object
    18      *        4: dup
    19      *        5: invokespecial #1                  // Method java/lang/Object."<init>":()V
    20      *        8: putfield      #3                  // Field o:Ljava/lang/Object;
    21      *        11: return
    22      * </pre>
    23      */
    24     public void m(){
    25         // 1. 分配内存
    26         // 2. 调用 Object 的构造函数,初始化对象
    27         // 3. 将对象内存的引用赋值给变量 o
    28         o = new Object();
    29     }
    30 }

    参考:

    https://en.wikipedia.org/wiki/Double-checked_locking
    https://javarevisited.blogspot.com/2014/05/double-checked-locking-on-singleton-in-java.html

  • 相关阅读:
    第八周课程总结&实验报告(六)
    第七周课程总结&实验报告(五)
    第六周&java实验报告四
    第五周课程总结&试验报告(三)
    第四周作业
    2019春总结作业
    第二周基础作业
    第三周作业
    2019期末总结
    第十四周课程总结 & 实验报告
  • 原文地址:https://www.cnblogs.com/kevin-yuan/p/13294314.html
Copyright © 2011-2022 走看看