Java理论与实践:切确操纵Volatile变量[Java编程]
本文“Java理论与实践:切确操纵Volatile变量[Java编程]”是由七道奇为您精心收集,来源于网络转载,文章版权归文章作者所有,本站不对其观点以及内容做任何评价,请读者自行判断,以下是其具体内容:
Java 语言中的 volatile 变量可以被看做是一种 “程度较轻的 synchronized”;与 synchronized 块相比,volatile 变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功效也仅是 synchronized 的一部份.本文介绍了几种有效利用 volatile 变量的情势,并夸大了几种不合适利用 volatile 变量的情形.
锁供应了两种主要特点:互斥(mutual exclusion) 和可见性(visibility).互斥即一次只答应一个线程持有某个特定的锁,因此可以利用该特点实现对同享数据的调和拜候协议,这样,一次就只有一个线程可以利用该同享数据.可见性要越发复杂一些,它必须确保释放锁之前对同享数据做出的更改关于随后得到该锁的另一个线程是可见的 —— 假如没有同步机制供应的这种可见性保证,线程看到的同享变量大概是改正前的值或不一致的值,这将引发很多严重问题.
Volatile 变量
Volatile 变量具有 synchronized 的可见性特点,但是不具有原子特点.这就是说线程可以自动发现 volatile 变量的最新值.Volatile 变量可用于供应线程安全,但是只能利用于非常有限的一组用例:多个变量之间大概某个变量的当前值与改正后值之间没有约束.因此,单独利用 volatile 还不足以实现计数器、互斥锁或任何具有与多个变量相关的不变式(Invariants)的类(比方 “start <=end”).
出于简易性或可伸缩性的考虑,您大概偏向于利用 volatile 变量而不是锁.当利用 volatile 变量而非锁时,某些习惯用法(idiom)越发易于编码和阅读.此外,volatile 变量不会像锁那样造成线程阻塞,因此也很少造成可伸缩性问题.在某些情形下,假如读操作远宏大于写操作,volatile 变量还可以供应优于锁的性能上风.
精确利用 volatile 变量的条件
您只能在有限的一些情形下利用 volatile 变量替换锁.要使 volatile 变量供应抱负的线程安全,必须同时满意下面两个条件:
对变量的写操作不依靠于当前值.
该变量没有包含在具有其他变量的不变式中.
实际上,这些条件表明,可以被写入 volatile 变量的这些有效值独立于任何程序的状况,包含变量的当前状况.
第一个条件的限制使 volatile 变量不能用作线程安全计数器.固然增量操作(x++)看上去近似一个单独操作,实际上它是一个由读取-改正-写入操作序列构成的组合操作,必须以原子方法履行,而 volatile 不能供应必须的原子特点.实现精确的操作需求使 x 的值在操作期间保持不变,而 volatile 变量无法实现这点.(但是,假如将值调整为只从单个线程写入,那么可以忽视第一个条件.)
大大都编程情形城市与这两个条件的此中之一冲突,使得 volatile 变量不能像 synchronized 那样广泛实用于实现线程安全.清单 1 显示了一个非线程安全的数值范围类.它包含了一个不变式 —— 下界老是小于或等于上界.
清单 1. 非线程安全的数值范围类
@NotThreadSafe
public class NumberRange {
private int lower, upper;
public int getLower() { return lower; }
public int getUpper() { return upper; }
public void setLower(int value) {
if (value > upper)
throw new IllegalArgumentException(...);
lower = value;
}
public void setUpper(int value) {
if (value < lower)
throw new IllegalArgumentException(...);
upper = value;
}
}
这种方法限制了范围的状况变量,因此将 lower 和 upper 字段定义为 volatile 范例不可以充分实现类的线程安全;从而仍旧需求利用同步.不然,假如刚巧两个线程在同一时间利用不一致的值履行 setLower 和 setUpper 的话,则会使范围处于不一致的状况.比方,假如初始状况是 (0, 5),同一时间内,线程 A 调用 setLower(4) 并且线程 B 调用 setUpper(3),明显这两个操作穿插存入的值是不符合条件的,那么两个线程城市通过用于保护不变式的查抄,使得最后的范围值是 (4, 3) —— 一个无效值.至于针对范围的其他操作,我们需求使 setLower() 和 setUpper() 操作原子化 —— 而将字段定义为 volatile 范例是无法实现这一目的的.
以上是“Java理论与实践:切确操纵Volatile变量[Java编程]”的内容,如果你对以上该文章内容感兴趣,你可以看看七道奇为您推荐以下文章:
本文地址: | 与您的QQ/BBS好友分享! |