一、volatile应用
1、volatile的定义
定义:Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致性更新,线程应该通过排它锁确保单独获得这个变量。
volatile在某些情况下比锁更加方便,如果一个字段被声明成volatile,Java线程模型确保所有线程看到的这个变量都是一致的。当一个线程修改这个变量时,其他线程能读到这个修改后的值。
2、volatile如何保证可见性的
在有volatile修饰的共享变量进行写操作时,其汇编代码中会有Lock前缀。Lock前缀在多核处理器中会引发以下两种事情:
- 将当前处理器缓存行的数据写回到系统内存中
- 这个写操作会使在其他CPU里缓存了改地址的数据无效
为了提高处理速度,处理器不直接和内存交互,而是先将系统内存中的数据读到内部缓存(L1,L2,L3或其他)后再进行操作,但操作完不知道何时写到内存。也即是Java内存模型
WEBRESOURCE3fe646e2cba8bce7f5fb1bbeb099f488.png · 资源文件 · 语雀
当对非 volatile 变量进行读写的时候,每个线程先从内存拷贝变量到CPU缓存中。如果计算机有多个CPU,每个线程可能在不同的CPU上被处理,这意味着每个线程可以拷贝到不同的 CPU cache 中。
而声明变量是 volatile 的,JVM 保证了每次读变量都从内存中读,跳过 CPU cache 这一步,并且它会导致其他CPU中对应的缓存行无效。每次修改之后会立即回写到内存中。
禁止进行指令重排序。
volatile没办法保证对变量的操作的原子性。所以当进行复合操作时会出现问题,如i++问题
测试代码:
public class VolatileDemo {
private volatile int i=0;
private void test(){
i++;
}
public static void main(String[] args) {
final VolatileDemo test=new VolatileDemo();
for (int i=0;i<10;i++){
new Thread(new Runnable() {
@Override
public void run() {
for (int j=0;j<1000;j++){
test.test();
}
}
}).start();
}
//保证前面的线程都执行完
while(Thread.activeCount()>2)
Thread.yield();
System.out.println(test.i);
}
}