Java内存模型

基本概念

1.并发编程模型

线程之间的通信机制有两种:共享内存和消息传递。Java的并发采用的是隐式的共享内存模型。
Java内存模型(JMM)是围绕着在并发过程中如何处理原子性、可见性和有序性来建立的。

  • 原子性:基本数据类型的访问读写是具备原子性的(针对long和double有非原子性协定),synchronized块之间的操作也具备原子性。
  • 可见性:当一个线程修改了共享变量(主内存中)的值,其他线程能够立即得知这个修改。
  • 有序性:线程内表现为串行的语义,从一个线程观察另一个线程,由于指令重排序和工作内存与主内存同步延迟,看到的操作是无序的。

2.内存模型抽象

  • 主内存:存储线程之间共享变量,所有存储在堆内存中的实例域、静态域和数组元素在线程之间共享。
  • 工作内存:线程私有,存储该线程读写共享变量的副本,它是JMM的抽象概念,并不存在,它涵盖了缓存、写缓冲区、寄存器及其他硬件和编译器的优化。
  • JMM抽象示意图:

3.重排序

在执行程序时,编译器和处理器会对指令重排序,重排序分三种类型:

  • 编译器优化的重排序
  • 指令级并行的重拍序
  • 内存系统的重排序
    编译器和处理器遵循as-if-serial语义(不管怎么重排序,都不能改变单线程程序的执行结果),不会对存在数据依赖关系的操作重排序。

4.内存屏障指令

JMM把内存屏障指令分为四类:

  • LoadLoad
  • LoadStore
  • StoreLoad
  • StoreStore

5.happens-before规则

在JMM中,如果一个操作执行的结果需要对另一个操作可见,这两个操作之间必须存在happens-before关系。
happens-before规则仅仅要求前一个操作的结果对后一个操作可见,且前一个操作按顺序排在第二个操作之前。

同步机制

1.volatile

对一个volatile共享变量的单个读写操作,相当于对普通共享变量的读写操作使用同一个锁来同步。
volatile保证了多线程操作变量的可见性(新值能立即同步到主内存,每次使用前立即从主内存刷新)和有序性(volatile本身就包含了禁止指令重排序的语义),但不能保证原子性(对任意单个volatile变量的读写具有原子性,但对于volatile++这种复合操作不具有原子性)。

2.锁

锁除了让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消息。
ReentrantLock的实现依赖于Java同步框架AQS,AQS使用一个整型的volatile变量state来维护同步状态。
锁释放和获取的内存语义的实现使用了volatile变量的读写所具有的内存语义和CAS自带的volatile读写的语义。

  • 公平锁:锁获取时先读volatile变量,锁释放时最后写volatile变量。
  • 非公平锁:锁获取时先使用CAS更新volatile变量,锁释放时最后写volatile变量。

3.final

final保证了可见性(被final修饰的字段在构造器中一旦初始化完成,并且构造器没有把this的引用传递出去,那在其他线程中就能看见final的值)。

参考书籍

《深入理解Java内存模型》、《深入理解Java虚拟机 第二版》

posted @ 2018-10-03 17:13  bkycrab  阅读(187)  评论(0编辑  收藏  举报