软件构造笔记-volatile关键字到底是啥
在老师讲课时,讲到synchronized关键字时,有些地方听的不是太清楚,在网上找资料的时候,还看到了另一个关键字volatile,看了好多篇博客,做了如下的总结。
一、volatile关键字简介
synchronized关键字是阻塞式同步,在线程竞争激烈的时候会逐渐由偏向锁膨胀为重量级锁。而volatile是JVM提供的最轻量级的同步机制。内存模型JMM告诉我们各个线程会将共享变量从主内存中拷贝到工作内存,然后执行引擎会基于工作内存中的数据进行操作处理。不过线程在工作内存中进行操作后将会何时写入主内存中,这个时机普通机制是没有规定的。
volatile一般用于修饰会被不同线程访问和修改的变量,而针对volatile修饰的变量给JVM给了规定:线程对volatile变量的修改会立刻被其他线程感知,即被volatile修饰的变量能够保证每个线程能够获取该变量的最新值,保证了数据的可见性。
二、volatile实现原理
加入volatile关键字的代码的class字节码中会多出了一个lock前缀指令,lock指令相当于一个内存屏障,主要做了三件事:
- 重排序时不能把后面的指令重排序到内存屏障之前的位置
- 将当前处理器缓存行的数据写回系统内存
- 这个写回内存的操作会使其他CPU里缓存的该内存地址的数据无效,即新写入的值对别的线程可见
经过这一波操作后,其他的线程发现自己工作内存中的缓存失效后,就会从内存中重新读取该变量数据,即保证了其他线程可以获取当前最新值。即可以说volatile实现了缓存一致性协议:每个处理器通过嗅探在总线上传播的数据来检查自己的缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器对这个数据进行修改操作的时候,会重新从系统内存中把数据读到处理器缓存里。
三、示例

3.1 对volatile的happens-before分析:
线程1先执行writer方法,然后线程2执行reader方法。
我们由happens-before规则推知,2 happens-before 3 (volatile变量的写happens-before于任意后续对volatile变量的读)
由传递性可以得知1 happens-before 4
由happens-before规则:如果A happens-before B,则A的执行结果对B可见,且A的执行顺序先于B的执行顺序
那么2的执行结果对3可见,也就是说线程1将flag修改为true,线程2能够迅速感知
3.2 volatile的内存语义分析:
如果线程1先进行writer方法,随后线程2进行reader方法。一开始的本地都是a和flag的初始化状态
在线程1线程2的本地内存里,线程1对初始值进行了修改并写入主内存中,而在线程2的本地内存里还是原来的值
由于volatile变量写后,线程中本地内存中共享变量就会置为失效状态,因此线程2需要再次从主内存中读取最新的共享变量值。从横向看,线程1和线程2进行了通信,线程1在写volatile变量的时候告诉线程2:你的本地内存中的值是旧的
线程2在读取volatile变量的时候就被告知目前自己的本地值是旧的,那线程2就只能去主内存中去取最新值了