Java语言还提供了另一种较弱形式的同步选择,即volatile变量,以确保更新的变量是可预见的传播到另一个线程。
当一个成员变量(field)被声明用volatile修饰时,编译器(the compiler)和运行时(runtime)都会注意到该变量是共享的,并且不会将该变量的操作与其它内存操作重新排序。
volatile变量不会被缓存在寄存器或者对其它处理器不可见的地方,因此在读取volatile修饰的变量时总会返回最新写入的值。
当访问volatile变量时不会执行加锁操作,这样就不会引起正在执行的线程阻塞,从而使得volatile变量是一种比synchronized关键字更轻量级的同步机制。
volatile变量对可见性的影响超出了volatile变量本身的价值。当线程A首先写入一个volatile变量,并且线程B随后读取该变量时,在写入volatile变量之前对线程A可见的所有变量的值,在线程B读取了volatile变量后,对线程B也是可见的。因此,从内存可见性的角度来看,写入volatile变量相当于退出同步代码块,而读取volatile变量就相当于进入同步代码块。可是,并不建议过度依赖volatile变量提供的可见性。如果在代码中依赖volatile变量来控制状态的可见性,通常比使用锁的代码更脆弱,也更难以理解。
仅当volatile变量能简化代码的实现,及验证你的同步策略时,才使用volatile变量。当验证正确性时需要对可见性进行复杂的判断(subtle reasoning),这种情况不要使用volatile变量。有效使用volatile变量的方式,包括确保自身状态的可见性,确保引用对象的可见性,或表明一个重要的生命周期事件(such as initialization or shutdown)的发生。
volatile变量的一种典型用法:检查某个状态标记以判断是否退出循环。如下代码示例,数绵羊。
volatile boolean asleep;
..
while (!asleep)
countSomeSheep();
volatile的局限性:加锁机制既可以确保可见性,又可以确保原子性;volatile变量只能确保可见性。
当且仅当满足以下所有条件时,才应该使用volatile变量:
- 对变量的写入操作不依赖变量当前的值,或者能确保只有单个线程更新变量的值。(Writes to the variable do not depend on its current value, or you can ensure
- 该变量不会与其它状态的变量,一起纳入到不变性条件中。(The variable does not participate in invariants with other state variables;)
- 当访问变量时,不需要任何加锁的理由。(Locking is not required for any other reason while the variable is being accessed.)