volatile是轻量级的synchronized,它在多处理开发中保证了共享变量的“
可见性”。
可见性是指当一个线程修改一个共享变量时,另一个线程能读到这个修改的值。
若是volatile变量修饰符使用恰当的话,它比synchronized的使用和执行成本更低。
成本低的缘由是:volatile不会引发上下文切换和调度。
一、volatile的定义与实现原理
Java编程语音容许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保经过排他锁单独得到这个变量。
若是一个字段声明为volatile,Java线程内存模型确保全部线程看到这个变量的值是一致的。
volatile如何保证可见性:
将当前处理器缓存行的数据写回到系统内存
这个写回内存的操做会使其余cpu里缓存了该内存地址的数据无效
为了提升处理速度,处理器不直接和内存进行通信,而是先将系统内存的数据读到内部缓存中后再进行操做,但操做完不知道什么时候会写回内存。若是对声明了volatile的变量进行写操做,JVM就会向处理器发一条lock前缀的指令,将这个变量所在缓存行的数据协会到系统内存。
在多核处理下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议,每一个处理经过嗅探在总线上传播的数据来检查本身缓存的值是否是过时了,当处理器发现本身缓存行对应的内存地址被修改时,就会将当前处理的缓存行设置为无效状态,当处理器对这个数据进行操做的时候,会从新从系统内存中把数据读处处理器缓存里。
二、volatile的使用优化
在JDK7中的并发包中新增了一个队列集合类LinkedTansferQueue,它在使用volatile变量时,使用了一种追加字节的方式来优化队列的出队和入队的性能。
LinkedTansferQueue这个类内部中定义了头节点head和尾节点tail,而在其内部有一个PaddedAtomicReference类,它的做用是将共享变量追加到64位。
为何追到64个字节会提供并发编程的效率?由于在一些处理器中,处理器的缓存L一、L2或者L3缓存的高速缓存行是64个字节的,而且不支持部分填充缓存行。这意味着在
队列的头节点和尾节点都不足64字节的状况下,处理器会将头节点和尾节点都读取到同一个缓存行中,在多处理器每一个处理器缓存行都会缓存一样的头、尾节点,当一个处理器试图修改头节点时,会将整个缓存行锁定,那么在缓存一致性机制的做用下,会致使其余处理器不能访问本身告诉缓存行中的尾节点,而队列的出队和入队操做则须要不停修改头节点和尾节点,因此在多处理器的状况下将会严重影响到队列的出对和入队操做。使用追加64字节的方式来填满高速缓存行,能够避免头节点和尾节点同时加载到一个缓存行中,使头、尾节点在修改时不会互相锁定。
追加64字节的方式不适用在缓存行非64字节的处理器中。
共享变量不会被频繁的写(若是共享变量不被频繁的写,锁的概率很是小,就没有必要追加64字节的方式避免项目锁定)