volatile和锁的内存语义与实现

1.volatile的内存语义与实现

1.1 volatile写读的内存语义

在介绍锁的内存语义之前,我们先简单介绍一下volatile写读的内存语义:

  • 当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存中。
  • 当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效,线程接下来将从主内存中读取共享变量

这两条保证了volatile能够达到它的即时可见的特性。
那JMM如何能够保证volatile实现其内存语义的,简单来说就是通过内存屏障。如果看过volatile变量汇编后的指令代码就会在代码中发现一句:

lock add1 $0x0

它的简单含义就是要把工作内存中的共享变量值刷新到主内存中,相当于加入内存屏障。

1.2 volatile有序的内存语义

volatile的另一个特性是禁止指令重排序,这里的内存语义我们可以总结为:

  • volatile读之后 的操作不会被重排序到 volatile读之前
  • volatile写之前 的操作不会被重排序到 volatile写之后
  • 先volatile写–后volatile读,不可重排序

JMM通过插入内存屏障来实现以上语义,实质上有四种内存屏障策略:

  • volatile写操作前插入StoreStore屏障
  • volatile写操作后插入StoreLoad屏障
  • volatile读操作前插入LoadLoad屏障
  • volatile读操作后插入LoadStore屏障

其中StoreLoad屏障是全能型屏障,可以完成其他3个屏障的功能。所以它被大部分CPU支持。不同CPU有着不同的重排序规则,但是这一套JMM屏障策略可以完成所有类型CPU下的volatile语义。例如对于x86的CPU,它本身只支持写-读操作的重排序,对读-读,读-写,写-写操作的重排序都不支持;那么我们只需要加入StoreLoad屏障来避免写-读操作的重排序即可实现volatile语义。

2.锁的内存语义与实现

理解了volatile的内存语义,锁的内存语义就会好理解了。

  • 当线程获取锁时,JMM会把线程对应的本地内存置为无效,然后临界区的代码从主存中读入共享变量到工作内存。
  • 当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中。

看完上面锁的内存语义,是不是感觉和volatile的内存语义很相像。对比锁和volatile的内存语义:

  • 锁的获取和volatile的读有相同的内存语义
  • 锁的释放和volatile的写有相同的内存语义

那锁的内存语义是如何实现的?
下一篇《ReentrantLock的源代码解析和锁的内存语义实现》将结合jdk源码进行分析。

参考:

  • 特别感谢《Java并发编程的艺术》
  • http://ifeve.com/volatile/

你可能感兴趣的:(JAVA)