Java内存模型之what,why,how

why

  我们先来聊聊一些背景知识,首先现代的计算机中,多任务和高并发是衡量一台计算机处理的能力重要指标之一,一个关键性的指标 TPS(transaction per second),一般能说明问题,它代表的是一秒内服务器平均能响应的请求数。
  其次,硬件的效率和一致性的方面,计算机的处理器运算速度与存储设备(内存,硬盘)的读写速度有几个数量级的差距,为了缓解这种差距,现在计算机加入高速缓存(cache)来作为内存与处理器之间的缓冲,其读写速度接近于 CPU,将运算需要的数据复制到缓存中,运算结束后在动缓存同步到内存中。但是事务的发展总是解决老问题带来新问题,高速缓存解决了处理器与内存之间的速度矛盾,带来了一个新的问题,缓存一致性(Cache Coherence)。

  尤其是在多处理器的系统中,每个处理器都有自己的高速缓存,而他们有共享同一个主存。多处理器系统缓存示意图和多级缓存结构如下:

多处理器系统缓存示意图

多级缓存结构图

  所以需要一种协议可以保障数据的一致性,目前主流的缓存一致性协议有 MSI, MESI, MOSI, Dragon Protocol, 要注意的是缓存都是有一个基础单位 cache line,这也是java中造成伪共享的根本原因。

  值得注意的是,为了处理器内部的运算单元被充分利用,处理器可能会对代码进行乱序执行(out of order execution) 优化,在 java 中,JIT 也是有执行重排序的优化,经典案例就是双重检查单例,要不要加 volatile 关键字。

what

所谓的 java 的内存模型的概念主要有以下几点:

  • 主要目标是定义各个变量的访问规则,即 jvm 中将变量存储到内存和从内存中取出变量这样底层细节
  • 规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存 (类比于处理器的高速缓存)
  • 线程的工作内存中保存了该线程使用到的变量到主内存副本拷贝
  • 线程对变量的所有操作(读取、赋值)都必须在工作内存中进行,而不能直接读写主内存中的变量
  • 不同线程之间无法直接访问对方的工作内存中的变量,线程间变量的传递需要在主存中来完成

java 内存模型结构图

how

最后,我们知道了 java 内存模型的一些定义和条件,那么怎样实现它的呢?主要有以下的几点:

  • 8种操作
    • lock
    • unlock
    • read
    • load
    • use
    • assign
    • store
    • write
  • happens-before 原则
  • 重排序
    从 java 源代码到最终实际执行的指令序列,经过三种排序
    java内存模型指令重排阶段
  • synchronized
  • volatile
  • final

  综上所述,我们对 java 内存模型的背景、概念、实现的过程有个总体上的理解。每一部分涉及到的细节还是很多的,以后再花时间深究。

作者

操先森

发布于

2021-09-17

更新于

2021-09-17

许可协议

评论