volatile关键字

讲volatile关键字的作用之前,先回顾下内存和cpu中间的cache的由来和作用。

我们知道在计算机中cpu的运算速度是最快的,内存其次,但因为内存读写速度往往跟不上cpu执行指令的速度,所以在cpu和内存之间添加高速缓存cache,将内存中的数据拷贝一份副本放到cache中,cpu运算时从cache中获取缓存数据,向缓存读写数据,操作完成后刷新缓存到内存。

但是,当多个线程访问cache中的共享变量(java中通常用static修饰变量,即全局变量)时,假设有一个线程更改了它,那么其他线程并不知道这一更新,导致共享变量最终结果不是全部的更新,即缓存不一致。那么如何解决缓存的一致性问题呢?

所以就出现了缓存一致性协议。最出名的就是Intel 的MESI协议,MESI协议保证了每个缓存中使用的共享变量的副本是一致的。它核心的思想是:当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,更新共享变量后会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读取。

volatile实际上也实现了和缓存一致性一样的功能,在某种程度上比synchronized(一个线程内的synchronized修饰的代码块或者对变量加同步锁内的代码块只有全部执行完毕,其他代码才会执行,即其他线程或者同一线程内的代码需要等待执行)更优,更能保证高并发场景下的数据一致性问题。

volatile具备两个作用:

  1. 保证数据的修改立即强制更新到主存,使其他线程的缓存失效,并更新缓存,即保证数据对其他线程的可见性。

  2. 禁用指令重排序(jvm对java代码指令重排,但并不是完全打乱代码的执行顺序,至少能保证代码块的执行最终结果是一致的,当具有依赖时先执行被依赖的变量的初始化)

禁用指令重排序,意味着对volatile修饰变量的操作前的代码一定先执行,后的代码一定后执行,即对volatile修饰变量的操作对其后的代码都是可见的,而不会发生指令重排序颠倒顺序执行。

volatile使用条件:
必须保证对volatile修饰变量的操作具备原子性,因为一旦不具备原子性,读取变量后假设线程进入阻塞状态,变量而未进行修改时,此时线程中的缓存并未失效,而其他线程对变量进行了修改,那么就出现了数据不一致的情况。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容