11.1.3 volatile关键词

关键词volatile用来对共享变量的访问进行同步。对一个volatile变量的上一次写入操作和下一次读取操作之间存在“在之前发生”的顺序。也就是说,上一次写入操作的结果对下一次读取操作是肯定可见的。在写入volatile变量值之后,CPU缓存中的内容会被写回主存;在读取volatile变量时,CPU缓存中的对应内容被置为失效状态,重新从主存中进行读取。将变量声明为volatile相当于为单个变量的读取和写入添加了同步操作。但是volatile在使用时不需要利用锁机制,因此性能要优于synchronized关键词。关键词volatile的主要作用是确保对一个变量的修改被正确地传播到其他线程中。最常使用volatile变量的一个场景是把作为循环结束的判断条件的变量声明为volatile。比如有两个线程A和B,线程A在一个循环中不断进行处理,线程B负责向线程A发送停止处理的信号。代码清单11-3中给出了这样的示例。线程A调用了Worker类的对象的work方法,开始执行具体的任务。在适当的时候,线程B会调用同一Worker类的对象的setDone方法来声明终止任务的执行。把done变量声明为volatile是很重要的。只有这样才能保证线程B对done变量所做的修改对于线程A的后续读取操作是可见的。否则,线程A可能由于无法看到done变量值的变化而一直运行下去。

代码清单11-3 使用volatile变量作为循环结束的判断条件


public class Worker{

private volatile boolean done;

public void setDone(boolean done){

this.done=done;

}

public void work(){

while(!done){

//执行任务

}

}

}


虽然volatile关键词使用简单,但是由于在实现时没有锁机制的存在,volatile关键词的适用场景是受限的。比如,对于代码清单11-1中的IdGenerator类,如果只是把域value声明为volatile,这样是不够的,仍然会出现问题。这是因为写入的value的正确值依赖于value的当前值,而当前值有可能是不正确的。代码清单11-3中要写入变量done的新值与该变量的当前值没有关系,使用volatile就足够了。