Java中volatile变量的用法

volatile是Java并发编程中控制变量可见性的一个关键字。

*要理解volatile,我们要从锁说起
锁提供了两种主要特性:互斥(mutual exclusion) 和可见性(visibility)。互斥即一次只允许一个线程持有某个特定的锁,因此可使用该特性实现对共享数据的协调访问协议,这样,一次就只有一个线程能够使用该共享数据。可见性要更加复杂一些,它必须确保释放锁之前对共享数据做出的更改对于随后获得该锁的另一个线程是可见的 —— 如果没有同步机制提供的这种可见性保证,线程看到的共享变量可能是修改前的值或不一致的值,这将引发许多严重问题。

*Volatile 变量具有 synchronized 的可见性特性
要解释可见性,需要从变量修改过程来看。假设我们的程序中有8个线程,跑在8个核心的机器上,并且每个线程恰好交由一个核心来跑(只是个假设哈)。其中一个线程负责写volatile修饰的共享变量,其他7个线程负责读。我们知道,为了提高系统的处理速度,cpu是有缓存的,很多读写操作都是直接跟cpu缓存交互,而不是和主内存交互。假设这7个读线程都已经在cpu缓存中缓存了这个共享变量,写线程要修改这个数据,咋办?写线程也是把这个数据从主内存读到cpu缓存,然后修改,再然后写回主内存,如果没有volatile修饰的话,此时读线程读到的还是cpu缓存中的数据,是脏数据。用volatile修饰之后呢,数据从cpu缓存写回到主内存的这个操作,会引起其他cpu里缓存了该内存地址的数据失效,既然失效了,读线程再使用这个变量的时候就只能从主内存再次获取了,就可以读取到写线程写入的最新值了。

*volatile不具备synchronized 的原子特性
所以不应该有多个线程同时写volatile变量,这是不安全的。所以我们经常把volatile应用到这样的场景下:一个线程负责写,其他所有线程负责读。举个例子:我们有一个全局的布尔状态标志,用于指示发生了一个重要的一次性事件,例如完成初始化或请求停机。我曾经在hadoop的datanode代码中读到类似的功能,它有一个全局的volatile变量来表示系统是否要shutdown,然后很多线程内部都是一个while(!is_shutdown){// do something, then sleep a while},默认is_shutdown = false; 当系统因为某些原因要停机的时候,某个线程设置is_shutdown = true; 然后所有的线程都受影响,都停掉了。

更多详细内容请参看下面的两篇参考资料,写得都非常非常好