目录
定义
在java规范中定义为:
Java
编
程
语
言允
许线
程
访问
共享
变
量,
为
了 确保共享变
量能被准确和一致地更新,
线
程
应该
确保通
过
排他
锁单
独
获
得
这
个
变
量。
Java
语
言 提供了volatile
,在某些情况下比
锁
要更加方便。如果一个字段被声明成
volatile
,
Java
线
程内存 模型确保所有线
程看到
这
个
变
量的
值
是一致的。(简单的说就是读出来的值肯定是正确的,但是写不一定)。
特点
效率高快,更适合读操作,
缺点
不能保证写的准确性,volatile 仅能使用在变量级别
原理
被volatile修饰的变量进行写的时候,会多出一行汇编代码,Lock前缀的命令会发生两件事情
1、将当前处理器的缓存行的数据写回到系统内存。
2、同时这个写回内存的操作会使CPU里引用了相同的地址数据失效。
注意:一般Lock不会锁地址总线,只是锁住的CPU地址缓存,如果锁住总线,会导致其他线程无法运行,降低执行效率,造成CPU浪费
volatile锁的性能优化:(追加64字节)
Java
并
发编
程大
师
Doug lea
在
JDK 7
的并
发
包里新增一个
队
列集合
类
Linked- TransferQueue,它在使用
volatile
变
量
时
,用一种追加字
节
的方式来
优
化
队
列出
队
和入
队
的性能,这种方式看起来很神奇,他当中有一个内部类AtomicReference,这个内部类只做了一件事情,就是将共享变量追加到64字节。我们可以来计算下,一个对象的引用占4个字节,它追加了15个变量(共占60个字节),再加上父类的value变量,一共64个字节。
为什么追加64字节能够提高并发编程的效率呢?
因
为对于CPU的
L1
、
L2
或
L3
缓
存的高速
缓
存行是
64
个字
节宽
,不 支持部分填充缓
存行,
这
意味着,如果
队
列的
头节
点和尾
节
点都不足
64
字
节
的
话
,
处
理器会将 它们
都
读
到同一个高速
缓
存行中,在多
处
理器下每个
处
理器都会
缓
存同
样
的
头
、尾
节
点,当一 个处
理器
试图
修改
头节
点
时
,会将整个
缓
存行
锁
定,那么在
缓
存一致性机制的作用下,会
导
致 其他处
理器不能
访问
自己高速
缓
存中的尾
节
点,而
队
列的入
队
和出
队
操作
则
需要不停修改
头
节
点和尾
节
点,所以在多
处
理器的情况下将会
严
重影响到
队
列的入
队
和出
队
效率。
Doug lea
使 用追加到64
字
节
的方式来填
满
高速
缓
冲区的
缓
存行,避免
头节
点和尾
节
点加
载
到同一个
缓
存 行,使头
、尾
节
点在修改
时
不会互相
锁
定。
也并不是所有的volatile变量都需要追加到64字节。
1、当缓存不是64字节处理器的时候/ 2、或者变量不需要频繁写的时候