先给出一个总体的结论
volatile保证并发要解决的三大问题中的可见性和有序性
另外,保证对单次读/写的原子性(复合运算不保证)
前置知识
JMM模型,见之前的一篇:https://www.cnblogs.com/yb38156/p/9427181.html
happens-before
定义: Two actions can be ordered by a happens-before relationship.If one action happens before another, then the first is visible to and ordered before the second. 规则: Each action in a thread happens before every subsequent action in that thread. • An unlock on a monitor happens before every subsequent lock on that monitor. • A write to a volatile field happens before every subsequent read of that volatile. • A call to start() on a thread happens before any actions in the started thread. • All actions in a thread happen before any other thread successfully returns from a join() on that thread. • If an action a happens before an action b, and b happens before an action c, then a happens before c.
定义讲的是:
如果a happen-before b,则a所做的任何操作对b是可见的(这里的before不是时间意义上的先后)
规则讲的是:
- 同一个线程中,前面的操作 happen-before 后续的操作(即单线程内按代码顺序执行。但是,在不影响单线程
环境执行结果的前提下,编译器和处理器可以进行指令重排序,这是合法的。换句话说,这一规则无法保证编译重排和指令重排)
- 监视器上的解锁操作 happen-before 其后续的加锁操作。(Synchronized 规则)
- 对volatile变量的写操作 happen-before 后续的读操作。(volatile 规则)
- 线程的start() 方法 happen-before 该线程所有的后续操作。(线程启动规则)
- 线程所有的操作 happen-before 其他线程在该线程上调用 join 返回成功后的操作
- 如果 a happen-before b,b happen-before c,则a happen-before c(传递性)
运行原理
保证可见性
线程本身并不直接与主内存进行数据的交互,而是通过线程的工作内存来完成相应的操作
操作主存中的变量时,每个线程会拷贝一个该变量的副本
当volatile修饰的变量在一个线程中被修改后,会立即强制刷新到主存,并使得其他线程中读取的该变量的副本失效
其他线程再次读取使用该变量时需要重新从主存中加载一份
保证有序性
体现在上面happens-before规则的第三条:对volatile变量的写操作 happen-before 后续的读操作
JVM通过添加"内存屏障"保证volatile的可见性和有序性
对volatile变量进行写操作时,前后分别加StoreStore和StoreLoad屏障
对volatile变量进行读操作时,在读操作之后加LoadLoad和LoadStore屏障
典型应用
java.util.concurrent.atomic包下的各种AtomicBoolean,AtomicInteger中实际保存值的变量,均定义为volatile的
例如AtomicBoolean:
public class AtomicBoolean implements java.io.Serializable { private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicBoolean.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } private volatile int value; public AtomicBoolean(boolean initialValue) { value = initialValue ? 1 : 0; } public final boolean compareAndSet(boolean expect, boolean update) { int e = expect ? 1 : 0; int u = update ? 1 : 0; return unsafe.compareAndSwapInt(this, valueOffset, e, u); } ... }
在很多其他的同步类的源码中,例如ConcurrentHashMap等,也可以看到他的影子
程序员单独在业务中使用,只在单例模式中看到过,不过自己写单例的情况,也早已经被Spring取代了