JMM针对编译器对编译器指定的volatile重排序规则表
是否能重排序第二个操作第一个操作普通读/写volatile读volatile写普通读/写NOvolatile读NONONOvolatile写NONO当第二个操作为volatile写,不管第一个操作是什么都不能进行重排序。确保volatile写之前的操作不会被编译器重排序到volatile写之后。
当第一个操作位volatile读,不管第二个操作是什么都不能进行重排序。确保volatile读之后的操作不会被编译器重排序到volatile读之前。
当第一个操作是volatile写,第二个操作是volatile读或者volatile写时不能重排序。语义
为了实现volatile的内存语义,编译器在生成字节码时,会在指令序列插入内存屏障来禁止特定类型的处理器重排序。下面是基于JMM内存屏障插入策略:
在每个volatile写操作的前面插入一个StoreStore屏障在每个volatile写操作的后面插入一个StoreLoad屏障在每个volatile读操作的后面插入一个LoadLoad屏障在每个volatile读操作的后面插入一个LoadStore屏障volatile写插入内存屏障指令序列示意图
普通读普通写StoreStore屏障(禁止上面的普通写与下面的volatile写重排序)volatile写StoreLoad屏障(禁止上面的volatile写与下面可能有的volatile读/写重排序)volatile读插入内存屏障指令序列示意图
volatile读LoadLoad屏障(禁止下面的普通读和上面的volatile读重排序)LoadStore屏障(禁止下面的普通写和上面的volatile读重排序)普通读普通写锁可以让临界区互斥执行。
当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存。
当线程获取锁时,JMM会把该线程对应的本地内存置为无效。从而使得被监视器保护的临界区代码必须从主内存中读取共享变量。
线程A释放锁,实质上是线程A向接下来将要获取这个锁的某个线程发出了(线程A对共享变量所做修改的)消息。
线程B获取锁,实质上是线程B接收了之前某个线程发出的(在释放这个锁之前对共享变量所做修改的)消息。
线程A释放锁,随后线程B获取锁,这个过程实质上是线程A通过主内存向线程B发送消息。
释放锁与volatile写有相同的内存语义,获取锁与volatile读有相同的内存语义。
写final域的重排序规则禁止把final域的写重排序到构造函数之外。这个规则可以保证,在对象引用为任意线程可见之前,对象的final域已经被正确初始化过了,而普通域不具有这个保证。
读final域的重排序规则是:在一个线程中,初次读对象引用与初次读对象包含的final域,JMM禁止重排序这两个操作。这个规则可以保证,在读一个对象的final域之前,一定会先读包含这个final域的对象的引用。
