volatitle
Volatile实现原理
参考博客:https://www.cnblogs.com/chenssy/p/6379280.html
synchronized是一个重量级的锁,虽然JVM对它做了很多优化,而下面介绍的volatile则是轻量级的synchronized。如果一个变量使用volatile,则它比使用synchronized的成本更加低,因为它不会引起线程上下文的切换和调度。Java语言规范对volatile的定义如下:
Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量。
上面比较绕口,通俗点讲就是说一个变量如果用volatile修饰了,则Java可以确保所有线程看到这个变量的值是一致的,如果某个线程对volatile修饰的共享变量进行更新,那么其他线程可以立马看到这个更新,这就是所谓的线程可见性。
在并发编程中我们一般都会遇到这三个基本概念:原子性、可见性、有序性。
JMM比较庞大,不是上面一点点就能够阐述的。上面简单地介绍都是为了volatile做铺垫的。
volatile可以保证线程可见性且提供了一定的有序性,但是无法保证原子性。在JVM底层volatile是采用“内存屏障”来实现的。
上面那段话,有两层语义
保证可见性、不保证原子性
禁止指令重排序
volatile看起来简单,但是要想理解它还是比较难的,这里只是对其进行基本的了解。volatile相对于synchronized稍微轻量些,在某些场合它可以替代synchronized,但是又不能完全取代synchronized,只有在某些场合才能够使用volatile。使用它必须满足如下两个条件:
对变量的写操作不依赖当前值;
该变量没有包含在具有其他变量的不变式中。
volatile经常用于两个两个场景:状态标记两、double check
volatile的特性:
volatile可见性:对一个volatile的读,总可以看到对这个变量最终的写;
volatile原子性:volatile对单个读/写具有原子性(32位Long、Double),但是复合操作除外,例如:i++;
jvm底层采用“内存屏障”来实现volatile语义。
volatile的内存语义及实现: 在JMM中,线程之间的通信采用共享内存来实现的。 volatile内存语义是:
当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值立即刷新到主内存中;
当读一个volatile变量时,JMM会把该线程对应的本地内存设置为无效,直接从主内存中读取共享变量。
volatile的底层实现是通过插入内存屏障,但是对于编译器来说,发现一个最优布置来最小化插入内存屏障的总数几乎是不可能的,所以,JMM采用保守策略。如下:
在每一个volatile写操作前面插入一个StoreStore屏障
在每一个volatile写操作后面插入一个StoreLoad屏障
在每一个volatile读操作后面插入一个LoadLoad屏障
在每一个volatile读操作后面插入一个LoadStore屏障
StoreStore屏障可以保证在volatile写之前,其前面的所有普通写操作都已经刷新到主内存中; StoreLoad屏障的作用是避免volatile写与后面可能有的volatile读/写操作重排序 LoadLoad屏障用来禁止处理器把上面的volatile读与下面的普通读重排序 LoadStore屏障用来禁止处理器把上面的volatile读与下面的普通写重排序
java中volatile关键字提供了一个功能,那就是被其修饰的变量在被修改后可以立即同步到主内存,被其修饰的变量在每次使用之前都从主内存刷新。因此,可以使用volatile来保证多线程操作时变量的可见性。
在java中,可以使用synchronized和volatile来保证多线程之间操作的有序性。实现方式有所区别:
volatile关键字会禁止指令重排;
2、synchronized关键字保证同一时刻只允许一条线程操作。 synchronized是万能,他可以同时满足三种特性,这其实也是很多人滥用synchronized的原因。
Last updated
Was this helpful?