volatile 原理

在多线程并发编程中synchronized和Volatile都扮演着重要的角色,Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”。可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。它在某些情况下比synchronized的开销更小,本文将深入分析在硬件层面上Inter处理器是如何实现Volatile的,通过深入分析能帮助我们正确的使用Volatile变量。

术语定义

共享变量 在多个线程之间能够被共享的变量被称为共享变量。共享变量包括所有的实例变量,静态变量和数组元素。他们都被存放在堆内存中,Volatile只作用于共享变量。
原子操作 Atomic operations 不可中断的一个或一系列操作。

Volatile的官方定义
Java语言规范第三版中对volatile的定义如下: java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致的更新,线程应该确保通过排他锁单独获得这个变量。Java语言提供了volatile,在某些情况下比锁更加方便。如果一个字段被声明成volatile,java线程内存模型确保所有线程看到这个变量的值是一致的。

为什么要使用Volatile
Volatile变量修饰符如果使用恰当的话,它比synchronized的使用和执行成本会更低,因为它不会引起线程上下文的切换和调度。

Volatile的实现原理

那么Volatile是如何来保证可见性的呢?在x86处理器下通过工具获取JIT编译器生成的汇编指令来看看对Volatile进行写操作CPU会做什么事情。
Java代码: instance = new Singleton();//instance是volatile变量
汇编代码: 0x01a3de1d: movb $0x0,0x1104800(%esi);0x01a3de24: lock addl $0x0,(%esp);

内存模型浅显分析

操作系统语义
计算机在运行程序时,每条指令都是在CPU中执行的,在执行过程中势必会涉及到数据的读写。我们知道程序运行的数据是存储在主存中,这时就会有一个问题,读写主存中的数据没有CPU中执行指令的速度快,如果任何的交互都需要与主存打交道则会大大影响效率,所以就有了CPU高速缓存。CPU高速缓存为某个CPU独有,只与在该CPU运行的线程有关。
有了CPU高速缓存虽然解决了效率问题,但是它会带来一个新的问题:数据一致性。在程序运行中,会将运行所需要的数据复制一份到CPU高速缓存中,在进行运算时CPU不再也主存打交道,而是直接从高速缓存中读写数据,只有当运行结束后才会将数据刷新到主存中。举一个简单的例子

主存有个变量i,在多个线程都执行++操作时,很明显都先拷贝到缓存中,然后CPU执行完后在刷回主存中,这样就有可能最后结果并不是两个线程合起来结果,例如原本i=0;两个线程i++后,i应该为2,但结果却是1
解决缓存一致性方案有两种:

  1. 通过在总线加LOCK#锁的方式
  2. 通过缓存一致性协议
    但是方案1存在一个问题,它是采用一种独占的方式来实现的,即总线加LOCK#锁的话,只能有一个CPU能够运行,其他CPU都得阻塞,效率较为低下。
    第二种方案,缓存一致性协议(MESI协议)它确保每个缓存中使用的共享变量的副本是一致的。其核心思想如下:当某个CPU在写数据时,如果发现操作的变量是共享变量,则会通知其他CPU告知该变量的缓存行是无效的,因此其他CPU在读取该变量时,发现其无效会重新从主存中加载数据。

volatile可以保证线程可见性且提供了一定的有序性,但是无法保证原子性。在JVM底层volatile是采用“内存屏障”来实现的。
上面那段话,有两层语义
3. 保证可见性、不保证原子性
4. 禁止指令重排序

观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令。lock前缀指令其实就相当于一个内存屏障。内存屏障是一组处理指令,用来实现对内存操作的顺序限制。volatile的底层就是通过内存屏障来实现的。下图是完成上述规则所需要的内存屏障:
volatile暂且下分析到这里,JMM体系较为庞大,不是三言两语能够说清楚的,后面会结合JMM再一次对volatile深入分析。

总结

volatile看起来简单,但是要想理解它还是比较难的,这里只是对其进行基本的了解。volatile相对于synchronized稍微轻量些,在某些场合它可以替代synchronized,但是又不能完全取代synchronized,只有在某些场合才能够使用volatile。使用它必须满足如下两个条件:
3. 对变量的写操作不依赖当前值;
4. 该变量没有包含在具有其他变量的不变式中。

猜你喜欢

转载自blog.csdn.net/qq_35711549/article/details/88291588
今日推荐