JMM学习笔记

摘要:JMM学习笔记

正文:

Java Memory Model

未同步的程序的行为

语句重排序

如上面例子所示

有 A B 两个共享变量,r1 r2 两个局部变量

要出现 May observer r2 == 2,r1 == 1 线程执行顺序应该是如下所示:

Thread 1:B=1
Thread 2:r1=B
Thread 2:A=2
Thread 1:r2=A

而按照程序员视角看第一个原始指令与实际的结果,可能会感觉很困惑,其根本原因是Java 的语义允许编译器和微处理器进行优化,这则会影响未正确同步的代码的行为 (如上代码是未同步的)。

非正式语义

看一个程序是否被正确的同步有两个关键概念

冲突访问

对共享元素存在数据竞争场景,如上文中的未同步程序的行为

Happens-Before 关系

happens-before 关系可以对两个动作进行排序,如果一个动作 happens-before 另一个动作,则第一个对第二个可见,且第一个排在第二个之前。必须强调的是,两个动作之间存在 happens-before 关系并不意味着这些动作在Java中必须以这种顺序发生。happens-before 关系主要用于强调两个有冲突的动作之间的顺序,以及定义数据争用的发生时机

如果某个动作A happens-before 动作B,且B happens-before 动作C,则有A happens-before C

顺序一致性

顺序一致性是程序执行过程中可见性和顺序的强有力保证,在顺序一致性内存模型中,每个操作都必须是原子执行且对所有线程可见

final 字段

声明为 final 的字段在 对象完全初始化后的时刻 值是不可变的,所以 final 字段也允许编程人员在不需要同步的情况下实现线程安全的不可变对象

什么是内存模型

内存模型可以看做为一组规则,规定了一个线程的写操作何时会对另一个线程可见

定义

Shared variables/Heap memory

能够在线程间共享的内存称为共享内存或堆内存,所有的实例字段,静态字段以及数组元素都存储在堆内存中,方法中的局部变量永远不会在线程间共享且不会被内存模型影响

Inter-thread Actions

线程间的动作是某一个线程执行的动作能被另一个线程探测或影响,比如lock某个管城、读写volatile变量

我们无需关心 Intra-thread 动作(线程内部) ,每个单线程需要遵守正确的 Intra-thread semantics

Intra-thread semantics

线程内语义是单线程程序的标准语义,线程内语义决定着某个线程孤立的执行过程,当从堆中读取值时,值是由内存模型决定的

Synchronization Actions

同步动作包括锁、解锁、读写 volatile 变量等动作

近似模型

在上文的非正式语义中描述了顺序一致性,在顺序一致性内存模型中,每个操作都必须是原子执行且对所有线程可见。其约束较严格,禁止了编译器和处理器优化,不适合做 Java 内存模型

Java中,共享变量(实例、静态、数组元素)存在堆内存中,堆内存在线程之间共享。非共享变量(局部变量、方法定义参数、异常处理参数)不会在线程之间共享,它们不会有内存可见性的问题

在JMM(Java Memory Model,java 内存模型)中,其