什么是JMM
- Java定义的
**并发编程操作的一组规范**
,除了抽象出**主内存与线程之间的关系**
以外,还规定了**从Java代码到Cpu指令转换过程**
的规定。 - 制定这些规定是为了
**简化程序员在并发环境下的开发**
,因为Cpu多级缓存的模型和指令重排序的优化会影响并发编程下的结果,通过制定相关的规范,程序员可以通过实现了相关规范的关键字例如(Synchronized、Volatile等)从而更方便的写出安全的程序。
内存模型图
- 程序不会直接操作主内存,会先将共享变量拷贝到线程的本地内存中,进行修改,再将修改后的共享变量副本刷入到主内存中。
- JMM负责定义主内存和线程之间的操作.
- 线程之间无法直接进行数据的交换,必须将数据写入到主内存中,其他线程从主内存进行读取的方式来进行线程间的数据交换。
JMM数据原子操作
- read(读取):作用于主内存中的变量,从主内存中读取数据
- load(载入):将从read操作中读取的数据加载到本地内存中
- use(使用):工作线程读取本地内存中的值
- assign(赋值):工作线程将处理后的值赋值到本地内存中
- store(存储):将本地内存中的数据写入到主内存中
- write(写入):将store入主内存中的数据写入到共享变量中
- lock(加锁):将主内存变量标记为线程独占状态
- unlock(解锁):将主内存变量从线程独占状态解除
并发编程三大特性
可见性
有序性
原子性
Happens-before和as-if-serial
Happens-before
- JMM提供
**Happens-before**
原则,在**多线程环境**
下也能够保证提供保证**有序性**
和**可见性**
。 - 定义为如果线程A与线程B中存在
**Happens-before**
关系,则线程A所做的操作 线程B可见,并且线程A的操作在线程B的操作之前执行。
Happens遵守的规则
**程序顺序规则**
:一个线程中的每个操作,happens-before于该线程中的任意后续操作。**监视器锁规则**
:对一个锁的解锁,happens-before于随后对这个锁的加锁。**volatile变量规则**
:对一个volatile域的写,happens-before于任意后续对这个volatile域的读。**传递性规则**
:如果A happens-before B,且B happens-before C,那么A happens-before C。**start()规则**
:如果线程A执行操作ThreadB.start()(启动线程B),那么A线程的ThreadB.start()操作happens-before于线程B中的任意操作。**join()规则**
:如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。
as-if-serial
制定规则,无论通过任何规则的重排序,需要保证在**单线程**
环境下,重排序后的代码执行结果与重排序前相等。
Synchronized
Synchronized获取锁的过程
- 获取锁
- 清空本地内存
- 将主内存中的变量拷贝到本地内存中
- 执行代码块
- 将本地内存中的变量刷新到主存中
- 释放锁
- Synchronized会在代码块前后添加内存屏障,保证代码块的内容不会重排序
原子性
- 在添加了Synchronized后,会在代码的前后增加
**monitorenter**
和**monitorexit**
- 从而在执行代码块时需要
**先获取到锁**
- 因为在同一个时间段内
**只能有一个线程**
获取到锁,因此**同一时间段内只会有一个线程**
执行代码块,从而能够保证原子性
有序性
- Synchronized不能够保证线程内的不进行指令重排序
- 但因为Synchronized确保了代码块只能由一个线程执行,而指令重排序的
**as if serial**
原则确保了,在单线程环境下不改变结果,因此Synchronized能够保证有序性
可见性
- 在
**释放锁**
前会将本地内存中的变量**刷入到主内存**
中,保证其他线程能够读取到最新的数据 - 在
**获取锁前**
,会**先清空本地内存**
中的数据,保证读取到的都是主内存中最新的数据 - Synchronized只能够保证,获取
**同一个Monitor锁**
的线程的可见性,对于不是同一个Monitor锁的线程**不能**
够保证可见性
Volatile
可见性
volatile做了什么事
**volatile**
修饰的变量,在线程A修改完后,会**第一时间刷新到主内存**
中- 线程BCD在读取该变量时,会通过
**读写栅栏**
(汇编lock关键字)的方式,来起到从主内存直接读取变量的效果 - 读写栅栏是一条CPU指令,作用是保证指令的有序性,不会出现重排序现象,同时保证该指令前的写操作能够顺利刷入到主内存中
可见性原理
- Volatile的底层是通过
**C语言**
实现的,在底层的**汇编语言**
中,在对**Volatile关键字修饰的变量**
操作时,会在前加入**lock**
汇编指令。Lock汇编指令能够保证读取Volatile修饰的变量值时,读取到的是最新的值 - lock 汇编指令的实现原理是在硬件层面上的
**缓存一致性协议**
缓存一致性协议(MESI)
- 当线程修改了
**本地内存**
中的变量后,需要马上将修改后的变量写入到**主内存**
中 - CPU和主内存之间的所有数据交互都需要经过
**BUS消息总线**
- 其他的线程通过
**总线嗅探机制**
监听到数据发生变化,从而让自己线程中的**本地内存中的变量失效**
- 因此其他线程在主内存的数据被修改后,都会重新到主内存中读取数据,因此读取到的都是新的数据
有序性
Java规范定义的内存屏障
JVM解析字节码时对Volatile变量的操作
- volatile变量在
**读操作**
时,会调用**OrderAccess::load_acquire**
,**acquire**
操作是**LoadLoad**
和**LoadStore**
的结合 - load_acquire可以看成先
**load**
命令读取,再添加**LoadLoad**
和**LoadStore**
屏障 - volatile变量在
**写操作**
时,会调用**OrderAccess::release_store**
,**release**
操作是**LoadStore**
和**StoreStore**
的结合。因此**release_store**
可以看成是,先添加**LoadStore**
和**StoreStore**
屏障,再**store**
写入主内存 - 在后面还调用了
**OrderAccess::storeload**
,也就是在**store**
命令写入主内存后,还添加了**StoreLoad**
屏障 - 应当后面有Volatile变量读时才需要加入StoreLoad,后面添加StoreLoad屏障是为了保险
if(属性读指令操作){
if(属性被volatile修饰){
OrderAccess::load_acquire
}else{
正常读
}
......
}else if(属性写指令操作){
if(属性被volatile修饰){
OrderAccess::release_store
OrderAccess::storeload
}else{
正常写
}
}
汇编对内存屏障的实现
- 只有
**多核CPU**
才会添加内存屏障,单核CPU不会也不需要添加内存屏障,因为没有多线程操作 - 内存屏障中,只有
**StoreLoad**
有具体实现,底层添加的是**lock**
指令,因此在汇编中有**lock前缀**
,可以等价于是添加了**StoreLoad**
屏障 - 其他的屏障只有默认实现,实际是memory指令,表明的是编译屏障,表明编译器不能将memory前后的指令混合在一起
inline void OrderAccess::storeload() { fence(); }
inline void OrderAccess::fence() {
//如果是多核CPU(multi-processing)
if (os::is_MP()) {
// always use locked addl since mfence is sometimes expensive
#ifdef AMD64
__asm__ volatile ("lock; addl $0,0(%%rsp)" : : : "cc", "memory");
#else
__asm__ volatile ("lock; addl $0,0(%%esp)" : : : "cc", "memory");
#endif
}
}
inline void OrderAccess::loadload() { compiler_barrier(); }
inline void OrderAccess::storestore() { compiler_barrier(); }
inline void OrderAccess::loadstore() { compiler_barrier(); }
inline void OrderAccess::storeload() { fence(); }
inline void OrderAccess::acquire() { compiler_barrier(); }
inline void OrderAccess::release() { compiler_barrier(); }
static inline void compiler_barrier() {
__asm__ volatile ("" : : : "memory");
}
原子性
Volatile不能保证原子性