JAVA中volatile关键字,在许多场合是不推荐使用的。因为,它容易使用不当导致不必要的问题。但是,如果使用得当,它能提供比锁更优的性能。
volatile单词的英文翻译为:易变的。它在JAVA中的语义就是来标明该变量是容易变化的,
提醒每次当线程读取该变量的时候都要从主内存中同步该变量的值。
在JAVA中有人把volatile看做是“程度较轻的 synchronized
”。与synchronized相比,volatile所需要的读写开销要小很多,但是它实现的功能也只有synchronized的一部分。锁提供了两种特性:互斥与可见性。互斥即为线程间的互斥访问,一次只允许一个线程持有某个特定的锁;可见性是指在线程对数据的修改,对后面访问的线程是可见的。Volatile 变量具有 synchronized
的可见性特性,但是不具备原子特性。这就是说线程能够自动发现 volatile 变量的最新值。
您只能在有限的一些情形下使用 volatile 变量替代锁。要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:
- 对变量的写操作不依赖于当前值。
- 该变量没有包含在具有其他变量的不变式中。
第一个条件的限制使 volatile 变量不能用作线程安全计数器。虽然增量操作(x++
)看上去类似一个单独操作,实际上它是一个由读取-修改-写入操作序列组成的组合操作,必须以原子方式执行,而 volatile 不能提供必须的原子特性。实现正确的操作需要使 x
的值在操作期间保持不变,而 volatile 变量无法实现这点。(然而,如果将值调整为只从单个线程写入,那么可以忽略第一个条件。)
很多并发性专家事实上往往引导用户远离 volatile 变量,因为使用它们要比使用锁更加容易出错。然而,如果谨慎地遵循一些良好定义的模式,就能够在很多场合内安全地使用 volatile 变量。要始终牢记使用 volatile 的限制 —— 只有在状态真正独立于程序内其他内容时才能使用 volatile —— 这条规则能够避免将这些模式扩展到不安全的用例。
也许实现 volatile 变量的规范使用仅仅是使用一个布尔状态标志,用于指示发生了一个重要的一次性事件,例如完成初始化或请求停机。
很多应用程序包含了一种控制结构,形式为 “在还没有准备好停止程序时再执行一些工作”,
volatile boolean shutdownRequested;
...
public void shutdown() { shutdownRequested = true; }
public void doWork() {
while (!shutdownRequested) {
// do stuff
}
}
很可能会从循环外部调用
shutdown()
方法 —— 即在另一个线程中 —— 因此,需要执行某种同步来确保正确实现
shutdownRequested
变量的可见性。(可能会从 JMX 侦听程序、GUI 事件线程中的操作侦听程序、通过 RMI 、通过一个 Web 服务等调用)。然而,使用
synchronized
块编写循环要比使用清单 2 所示的 volatile 状态标志编写麻烦很多。由于 volatile 简化了编码,并且状态标志并不依赖于程序内任何其他状态,因此此处非常适合使用 volatile。
这种类型的状态标记的一个公共特性是:通常只有一种状态转换;shutdownRequested
标志从 false
转换为 true
,然后程序停止。这种模式可以扩展到来回转换的状态标志,但是只有在转换周期不被察觉的情况下才能扩展(从 false
到 true
,再转换到false
)。此外,还需要某些原子状态转换机制,例如原子变量。