Java内存模型定义了线程与实际计算机系统内存的合法交互。在某种程度上,它描述了在多线程代码中哪些行为是合法的。它确定线程何时能够可靠地看到其他线程对变量的写入。它定义了volatile、final和synchronized的语义,保证了线程间内存操作的可见性。
让我们先讨论一下内存屏障,这是我们进一步讨论的基础。JMM(Java Memory Model)中有两种类型的内存屏障指令:读屏障和写屏障。
读取屏障使本地内存(缓存、寄存器等)失效,然后从主内存中读取内容,以便其他线程所做的更改对当前线程可见。写屏障将处理器本地内存的内容刷新到主内存,这样当前线程所做的更改对其他线程可见。
synchronized的JMM语义
当一个线程通过进入一个同步的代码块获得一个对象的监视器时,它执行一个读取屏障(使本地内存失效,而从堆中读取)。类似地,从同步块退出作为释放关联的监视器的一部分,它执行写入屏障(刷新主存储器的改变),从而使用一个线程的同步块对共享状态进行修改,保证对其他线程的后续同步读取可见。这种保证是由JMM在同步代码块存在的情况下提供的。
Volatile的 JMM 语义
对易失性变量的读写具有与使用同步代码块获取和释放监视器相同的内存语义。因此,JMM保证了挥发性场的可见性。此外,在Java 1.5之后,volatile读写不能与任何其他内存操作(volatile和non-volatile两者)一起重新排序。因此,当线程A写入可变变量V,然后线程B读取可变变量V时,在写入V时A可见的任何变量值现在都保证B可见。
让我们试着用下面的代码来理解它。
Data data = null;
volatile boolean flag = false;
Thread A
-------------
data = new Data();
flag = true; <-- writing to volatile will flush data as well as flag to main memory
Thread B
-------------
if(flag==true) { <-- reading from volatile will perform read barrier for flag as well data.
use data; <--- data is guaranteed to visible even though it is not declared volatile because of the JMM semantics of volatile flag.
}
网友评论