Java内存模型中,有主内存和每个线程各自的工作内存,虚拟机和硬件可能会让线程工作内存优先存储于寄存器和高速缓存中,以提高性能。
所有变量都存储在主内存中,线程工作内存中保存了此线程使用到的变量的副本。工作内存在线程之间是隔离的,对其他线程不可见。线程对变量的所有操作都必须在工作内存中进行,修改后的变量副本要写回主内存。这样就会出现同一个变量在某个瞬间,在一个线程的工作内存中的值可能与另外一个线程工作内存中的值,或者主内存中的值不一致的情况。
Volatile 变量
Volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。这就是说线程能够自动发现 volatile 变量的最新值。Volatile 变量可用于提供线程安全,但是只能应用于非常有限的一组用例:多个变量之间或者某个变量的当前值与修改后值之间没有约束。因此,单独使用 volatile 还不足以实现计数器、互斥锁或任何具有与多个变量相关的不变式(Invariants)的类(例如 “start <=end”)。
出于简易性或可伸缩性的考虑,您可能倾向于使用 volatile 变量而不是锁。当使用 volatile 变量而非锁时,某些习惯用法(idiom)更加易于编码和阅读。此外,volatile 变量不会像锁那样造成线程阻塞,因此也很少造成可伸缩性问题。在某些情况下,如果读操作远远大于写操作,volatile 变量还可以提供优于锁的性能优势。
一个变量声明为volatile,就意味着这个变量被修改时其他所有使用到此变量的线程都立即能见到变化(称之为可见性)。具体是在每次使用前都要先刷新,以保证别的线程中的修改已经反映到本线程工作内存中,因此可以保证执行时的一致性。以下例子展现了volatile的作用:
public class StoppableTask extends Thread {
private volatile boolean pleaseStop;
public void run() {
while (!pleaseStop) {
// do some stuff...
}
}
public void tellMeToStop() {
pleaseStop = true;
}
}
假如pleaseStop没有被声明为volatile,线程执行run的时候检查的是自己的副本,就不能及时得知其他线程已经调用tellMeToStop()修改了pleaseStop的值。
Volatile一般情况下并不能代替sychronized,因为volatile不能保证操作的原子性,即使只是i++,实际上也是由多个原子操作组成:read i; inc; write i,假如多个线程同时执行i++,依然可能由于不同线程交替执行而出现写入脏数据的情况。也就是说,如果对变量值的修改需要依赖于变量之前的值,那么volatile不能保证一致性,需要用sychronized,或者使用atomic类型(java.util.concurrent.atomic.*);而上面的代码例子是可以使用volatile的典型场景。