JAVA 内存模型(一)-阿里云开发者社区

开发者社区> 开发与运维> 正文

JAVA 内存模型(一)

简介:

综述:

 简单的说,Java的内存模型定义了在一个线程对一个共享变量进行修改后,修改后的共享变量什么时候对其它线程可见。

作用:

  1. 对于程序员

       JMM给程序员呈现出来的是具有顺序一致性的强内存模型。(通俗点就是所见即所得)
  2. 对于处理器与编译器

        JMM给处理器与编译器提供了一个比较弱的happens-before内存模型,这样尽可能多的给处理器与编译器优化代码的空间。
    

处理器与编译器的优化技术

  1. 编译器优化的重排序。

    编译器在不改变单线程程序语义的前提下,可以对我们编写的代码进行重排。如下:
    
public class Test { 
    public static void main(String args []) {
        int a = 1;       // 1
        int b = 2;      // 2
        // 在单线程语义下这个程序的输出为 1
        // 交换标注为 1,2 的两行代码不改变单线程程序语义
        // 编译器可以对这样的代码进行指令重排,以提高程序运行的性能
        System.out.println(a);
    }       
 }  
public class Test {
    public static void main(String args[ ]) {
        int a = 0;                          // 1
        a = 2;                              // 2
        a = 3;                              // 3
        // 在单线程语义下这个程序的输出为3
        // 如果标注为 2 ,3的代码进行交换顺序,那就改变了单线程语义,JMM
        // 是禁止这种重排序的。
        System.out.println(a);  
    }
}

2 . 处理器的指令级并行重排序

     现代处理器采用了指令级并行技术,来对多条指令进行重排序。如果指令间不存在数据依赖(<u>对同一个变量写后读,读后写,写后写</u>),那处理器就可以改变指令间的执行顺序。如下:
  0x00007f31c9108ac0: mov    %eax,-0x14000(%rsp)
  0x00007f31c9108ac7: push   %rbp
  0x00007f31c9108ac8: sub    $0x30,%rsp
  0x00007f31c9108acc: movabs $0x7f31c8c00448,%rdi  ;   {metadata(method data for {method} {0x00007f31c8c00260} 'test' '()V' in 'testTwo')}
  0x00007f31c9108ad6: mov    0xdc(%rdi),%ebx
  // 假设以上5条汇编指令之间不存在数据依赖,如果处理器的流水线大于等于5,那么,这5条汇编指令可以并行的进行计算。

3 . 内存系统的重排序

     由于处理器使用缓存,这使得加载和存储操作看上去是在乱序执行。如图[1]:

screenshot
intel i7 处理器是4核8线程,每个核心都有自己的L1, L2 缓存,当不同的核心缓存相同的共享变量,并写回到L3 缓存时,就存在内存系统的重排序。

Happens-before 规则

  综述:happens-before 关系是由Lamport(1978)这位大神提出的。它的表述为: a -> b 读作“a 在 b 之前发生”,意思是所有的进程(分布式系统中)/ 线(我们现在讨论的JMM)一致认为事件 a  先于事件 b 发生:*注意:这里的先发生实际上是一种可见性的表述,它并不代表在物理时间上事件a先于事件b发生,而是事件b在执行前要能看到事件a执行后的结果。*这就为处理器,与编译器的重排提供了保证。

我们来看看JSR-133对JMM中happens-before规则的示例:

1. 程序顺序规则:一个线程中的的每个操作,happens-before于该线程中的任意后续操作。
2. 监视器规则: 对一个锁的解锁,happens-before于随后对这个锁的加锁。
3. volatile变量规则:对一个volatile域的写,happens-before与任意后续对这个volatile域的读。
4. 传递性: 如果 a -> b, b -> c, 则 a -> c.
5. 线程启动 start() 规则:如果线程A 执行操作ThreadB.start()(启动线程B),那么A线程的启动线程的操作,happens-before于线程B中的任意操作。
6. join()规则: 如果线程A 执行ThreadB.join()并成功返回,那么线程B中的任意操作,happens-before 于线程A 从ThreadB.join()操作成功返回。

顺序一致性内存模型

综述:顺序一致性模型是一个被计算机科学家理想化了的理论参考模型,它为程序员提供了内存可见性的保证。
顺序一致性的两大特征:
1. 一个线程中所有操作必须按照程序的顺序来执行
2.不管程序是否同步,所有线程都只能看到一个单一的操作执行顺序。在顺序一致性内存模型中,每个操作都必须原子执行且对所有线程立即可见。

本博客是对《JAVA并发编程的艺术》方腾飞,魏鹏,程晓明 著。第三章的读后感。

参考资料:[1] intel开发技术手册卷3第2章

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
开发与运维
使用钉钉扫一扫加入圈子
+ 订阅

集结各类场景实战经验,助你开发运维畅行无忧

其他文章