zoukankan      html  css  js  c++  java
  • Volatile 原理及使用,java并发中的可见性问题

    1.解决并发编程中的可见性问题

    volatile 代表不使用cpu缓存,修改后的数据,将直接刷到内存中,被volatile修饰的变量,读取的时候,也是从内存中读取,不从cpu缓存中读取

    上代码

    
    // 以下代码来源于【参考1】
    class VolatileExample {
      int x = 0;
      volatile boolean v = false;
      public void writer() {
        x = 42;
        v = true;
      }
      public void reader() {
        if (v == true) {
          // 这里x会是多少呢?
        }
      }
    }
    

    例如上面的示例代码,
    假设线程 A 执行 writer() 方法,按照 volatile 语义,会把变量 “v=true” 写入内存;
    假设线程 B 执行 reader() 方法,同样按照 volatile 语义,线程 B 会从内存中读取变量 v,如果线程 B 看到 “v == true” 时,那么线程 B 看到的变量 x 是多少呢?
    直觉上看,应该是 42,那实际应该是多少呢?这个要看 Java 的版本,如果在低于 1.5 版本上运行,x 可能是 42,也有可能是 0;如果在 1.5 以上的版本上运行,x 就是等于 42。

    为什么呢?

    • 变量 x 可能被 CPU 缓存而导致可见性问题。这个问题在 1.5 版本已经被圆满解决了。Java 内存模型在 1.5 版本对 volatile 语义进行了增强。怎么增强的呢?答案是一项 Happens-Before 规则。

    这个规则就是:
    前面一个操作的结果对后续操作是可见的

    2.六项Happens-Before规则

    1. 程序的顺序性规则
    这条规则是指在一个线程中,按照程序顺序,前面的操作 Happens-Before 于后续的任意操作。这还是比较容易理解的,比如刚才那段示例代码,按照程序的顺序,第 6 行代码 “x = 42;” Happens-Before 于第 7 行代码 “v = true;”,这就是规则 1 的内容,也比较符合单线程里面的思维:程序前面对某个变量的修改一定是对后续操作可见的。

    2. volatile 变量规则

    对一个 volatile 变量的写操作, Happens-Before 于后续对这个 volatile 变量的读操作。

    3.传递性
    这条规则是指如果 A Happens-Before B,且 B Happens-Before C,那么 A Happens-Before C。

    对应到上面的源码中就是

    x=42 Happens-Before 写变量v=true 对应规则(程序的顺序性规则)
    写变量v=true Happens-Before 读变量if(v==true) 对应规则2(volatile变量规则)

    4.管程中的锁规则
    管程,就是同步愿语,java中指的就是synchronized

    上代码

    
    synchronized (this) { //此处自动加锁
      // x是共享变量,初始值=10
      if (this.x < 12) {
        this.x = 12; 
      }  
    } //此处自动解锁
    

    假设 x 的初始值是 10,线程 A 执行完代码块后 x 的值会变成 12(执行完自动释放锁),线程 B 进入代码块时,能够看到线程 A 对 x 的写操作,也就是线程 B 能够看到 x==12

    5.start()规则
    主线程 A 启动子线程 B 后,子线程 B 能够看到主线程在启动子线程 B 前的操作。
    上代码

    
    Thread B = new Thread(()->{
      // 主线程调用B.start()之前
      // 所有对共享变量的修改,此处皆可见
      // 此例中,var==77
    });
    // 此处对共享变量var修改
    var = 77;
    // 主线程启动子线程
    B.start();
    

    6.join()规则
    如果在线程 A 中,调用线程 B 的 join() 并成功返回,那么线程 B 中的任意操作 Happens-Before 于该 join() 操作的返回

    3.Java 内存模型

    Java 内存模型规范了 JVM 如何提供按需禁用缓存和编译优化的方法

    具体来说,这些方法包括 volatile、synchronized 和 final 三个关键字,以及六项 Happens-Before 规则

  • 相关阅读:
    1300 · 巴什博弈
    1 · A + B 问题
    37 · 反转一个3位整数
    145 · 大小写转换
    Spring-Boot整合freemarker引入静态资源css、js等
    Monkey API学习与脚本编写小记
    Mac终端快速启动uiautomatorviewer
    Mac解决使用uiautomatorviewer会报错:./uiautomatorviewer 03:56:07 E/Screenshot: Unsupported protocol: 2
    charles抓包https乱码问题解决-转载
    mac解决adb devices命令找不到真机设备
  • 原文地址:https://www.cnblogs.com/PythonOrg/p/14037531.html
Copyright © 2011-2022 走看看