volatile介绍

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

猜你喜欢

转载自www.cnblogs.com/use-D/p/9623502.html