您的位置:首页 > 编程语言 > Java开发

深入理解Java并发机制(2)--volatile关键字

2017-10-31 11:04 1081 查看
关键字volatile提供了轻量级的同步机制,在理解volatile关键字之前先来看几个支撑技术。

有序性的一种保证方式:内存屏障

内存屏障是一组指令,分4类,这些指令指定了其前后指令的排序规则。

屏障类型指令示例说明
LoadLoad BarrierLoad1;LoadLoad;Load2确保load1数据的装载先于load2及其后续所有load指令
StoreStore BarrierStore1;StoreStore;Store2确保Store1数据刷新到内存先于Store2及其后续所有store指令
LoadStore BarrierLoad1;LoadStore;Store2确保load1数据的装载先于Store2及其后续所有store指令
StoreLoad BarrierStore1;LoadLoad;Load2确保Store1数据刷新到内存先于load2及其后续所有load指令 。注意:StoreLoad Barriers会使该屏障之前的所有内存访问指令(包括load和store指令)都执行完了之后,再执行屏障之后的内存访问指令。
也就是说,在一个指令序列中插入了内存屏障,这些内存屏障会限制其前后指令的排序,而在两个内存屏障之间代码序列的重排序则没有约束。就好像屏障一样,被限制的指令无法越过。

同时也可以看出内存屏障将缓存值刷新到内存也起到了提供可见性的作用。

强大的汇编指令前缀:Lock

lock前缀有三方面的功能:

1. 确保对内存的读-改-写操作原子执行。

2. 禁止该指令,与之前和之后的读写指令重排序。

3. 将写缓冲区的所有数据刷新到内存中,并且这个写操作会使其他CPU里缓存了该内存地址的数据无效。

其中第2条和第3条共同起到了内存屏障的效果,并且是一并禁止了load和store指令。

可以看出,单单一个lock前缀就提供了原子性、有序性和可见性的三方面的可靠保证。

volatile修饰变量

被volatile修饰的变量有三个特性:

1. 原子性。对任意单个volatile变量的读、写具有原子性,但类似于volatile++这种复合操作没有原子性。

2. 可见性。对一个volatile的读,总是能看到任意线程对这个变量的最后一次写入。

3. 有序性。volatile禁止指令重排序。

之所以volatile具有这三个特性,在于对volatile变量的操作有lock prefix支持。lock prefix提供了三方面的特性保证。

锁与volatile

锁的释放与volatile写具有同样的内存语义:释放锁和对volatile变量的写,JMM都会把线程对应的本地内存中的共享变量刷新到主内存中。

锁的获取与volatile读具有同样的内存语义:获取锁和对volatile变量的读,JMM都会将线程对应的本地内存置为无效,而是从主内存中重新读取。

CAS与volatile

CAS操作具有volatile读和写的内存语义。该操作在执行cmpxchg指令之前,如果是多处理器,将会插入lock前缀。lock前缀提供了与volatile一致的内存语义。

现代操作系统

Java并发编程的艺术

深入理解Java虚拟机

Java并发编程实战

JSR-133

并发编程网

Intel IA-32
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息