《Java并发编程的艺术》第三章读书笔记——3.1 JAVA内存模型的基础

3.1.1 - 3.1.2 Java并发基础与内存模型

在并发编程中,需要处理两个问题:线程之间如何通信及线程之间如何同步。通信是指线程之间以何种机制来交换信息。在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递。
JAVA的并发采用的是共享内存模型,JAVA线程之间的通信是总是隐式进行。

JAVA线程之间的通信由JAVA内存模型控制(JMM),JMM决定一个线程对共享变量的写入何时对另一个线程可见。线程之间的共享变量存储在主内存中,每个线程都有一个私有的本地内存,本地内存中存储了该线程以读/写共享变量的副本。

《Java并发编程的艺术》第三章读书笔记——3.1 JAVA内存模型的基础_第1张图片
从上图看到,如果线程A和线程B之间要通信的话,必须要经过下面2个步骤

  1. 线程A把本地内存A中更新过的共享变量刷新到主内存中。
  2. 线程B到主内存中去读取线程A之前更新到主内存中的共享变量。
    《Java并发编程的艺术》第三章读书笔记——3.1 JAVA内存模型的基础_第2张图片
    JMM通过控制主内存与每个线程的本地内存之间的交互,来为JAVA程序员提供内存可见性保证。

3.1.3 从源代码到指令序列的重排序

在执行程序时,为了提高性能,编译器和处理器常常会对指令做重排序。重排序可分为3种类型:

  1. 编译器优化重排序。
  2. 指令级并行的重排序。
  3. 内存系统的重排序。

从JAVA源码最终实际执行的指令序列,会分别经历下面3种重排序

在这里插入图片描述
上图中1属于编译器重排序,2和3属于处理器重排序。这些重排序可能会导致多线程程序出现内存可见性问题。对于编译器,JMM的编译器重排序规则会禁止特定类型的编译器重排序。对于处理器重排序,JMM的处理器重排序规则会要求JAVA编译器在生成指令序列时,插入特定类型的内存屏障指令来禁止特定类型的处理器重排序。
JMM属于语言级的内存模型。

3.1.4 并发编程模型的分类

  1. 现代的处理器使用写缓冲区临时保存向内存写入的数据。
  2. 写缓冲区可以保证指令流水线持续运行,它可以避免由于处理器停顿下来等待向内存写入数据而产生的延迟。
  3. 通过以批处理的方式刷新写缓冲区,以及合并写缓冲区中对同一内存地址的多次写,减少对内存总线的占用。
  4. 每个处理器上的写缓冲区,只对它所在的处理器可见
  5. 处理器对内存的读、写操作的执行顺序,不一定与内存实际发生的读写顺序一致
处理器 线程A 线程B
代码 a = 1; //A1 ,x = b;// A2 b = 2;//B1 ,y = a;//B2
运行结果 初始状态:a=b=0;处理器允许执行后得到的结果:x = y = 0
假设处理器A和处理器B按程序的顺序并发执行内存访问,最终可能得到x=y=0的结果。

《Java并发编程的艺术》第三章读书笔记——3.1 JAVA内存模型的基础_第3张图片

此时有可能处理器A和B同时把共享变量写入到自己的写缓冲区(A1,B1),然后从内存中读取另一个共享变量(A2,B2),最后才把自己写缓存区中保存的脏数据刷新到内存中(A3,B3)。此时处理器的内存操作顺序被重排序了。

为了保证内存可见性,java编译器在生成指令序列的适当位置会插入内存屏障指令来禁止特定类型的处理器重排序。JMM把内存屏障指令分为4类:

  1. LoadLoad Barriers
  2. StoreStore Barriers
  3. LoadStore Barriers
  4. StoreLoad Barriers

StoreLoad Barriers是一个"全能型"屏障,同时具有其他三个屏障的效果

3.1.5 happens-before简介

在JMM中,如果一个操作的结果需要对另一个操作可见,那么这两个操作之间必须存在happens-before关系。这两个操作可以是一个线程内的,也可以是在不同线程之间的。

与程序员密切相关的happens-before规则如下:

  1. 程序顺序规则:一个线程中的每个操作,happens-before与该线程中的任意后续操作。
  2. 监视器规则:对一个锁的解锁,happens-before于随后对这个锁的加锁。
  3. volatile规则:对一个volatile域的写,happens-before于随后任意后续对这个域的读。
  4. 传递性:如果A happens-before B且B happens-before C,那么A happens-before C。

注意:两个操作之间具有happens-before关系,并不意味着前一个操作必须要在后一个操作之前执行!happens-before仅仅要求前一个操作(执行的结果)对后一个操作可见。

你可能感兴趣的:(并发编程艺术)