Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”。它在某些情况下比synchronized的开销小,因为它不会引起线程上下文的切换和调度。
访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞。
可见性的意思是当一个线程修改一个共享变量时,另一个线程能读到这个修改的值。
如果一个字段被声明为 volatile,java线程内存模型确保所有线程看到这个变量的值是一致的。
Volatile的实现原理
处理器为了提高处理速度,不直接和内存进行通讯,而是先将系统内存的数据读到内部缓存后再进行操作,但操作完成之后不知道何时会写到内存中,如果对声明了Volatile变量进行写操作,JVM就会向处理器发送一条Lock前缀的指令,将这个变量所在缓存行的数据写会到系统内存。
但是就算写回到内存,如果其他处理器缓存的值还是旧的,再执行计算操作就会有问题,所以在多处理器下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议。每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期的,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置为无效状态,当处理器要对这个数据进行修改操作时,会强制重新从系统内存里把数据读到处理器缓存里。
有volatile变量修饰的共享变量进行写操作的时候会多一行汇编代码,包含lock前缀的指令。
从内存可见性的角度来看,写入volatile变量相当于退出同步代码块,而读取volatile变量就相当于进入同步代码块。
并不建议过度依赖volatile变量提供的可见性。如果在代码中依赖volatile变量来控制状态的可见性,通常比使用锁的代码更脆弱。仅当volatile变量能简化代码的实现以及对同步策略的验证时,才应该使用它们。
public class CountOperate extends Thread {
//不需要加锁,通常用作某个操作完成/发生中断或状态的标志
volatile private boolean isRunning=true;
public void run(){
while(isRunning==true){
}
}
}
加锁机制既可以确保可见性又可以确保原子性,而volatile变量只能确保可见性。