Java - 一次未完成的翻译(Volatile变量)
Java theory and practice: Managing volatility
【原文地址:http://www.ibm.com/developerworks/library/j-jtp06197/】
参考:http://stackoverflow.com/questions/3786825/volatile-boolean-vs-atomicboolean
Java语言包含两种原生同步机制:同步块(同步函数)以及 Volatile变量。Java提供这两种方式用于编写线程安全代码。
Volatile 变量虽然是两者中较弱的,但是有时却更加简单并且花销更少,同时也更容易被错误使用。
Brian Goetz将通过这篇文章探讨正确使用Volatile变量的模式以及提供一些适用范围的注意事项。
【Volatile变量】
Java语言中的Volatile变量可以被认为是同步代码块的精简版(synchronized lite),相比于同步代码块(synchronized),Volatile具有更少代码量和更小的运行时花销;
但是,Volatile变量只能用以处理Synchronized所能从事的其中一部分。 这篇文章将呈现几种有效使用Volatile变量的情景以及什么情况下不能使用它们。
锁机制提供两大特点:互斥与可见性。互斥定义为同一时刻只能有一个线程可以获取锁,基于该属性可以实现协调访问共享数据,使得某一时刻只有一个线程将使用共享数据。
可见性定义为保证释放锁之前所做的更改对随后获得该锁的另一线程可见,如果没有同步机制提供的可见性,线程访问的共享变量值可能是无效或不一致的,这可能引发许多严重问题。
//todo
Volatile变量拥有同步机制的可见性特征,却没有拥有原子性特征;
你可能更喜欢使用volatile变量而不是锁的两个主要原因:简单和可扩展性。
有些惯用语法使用Volatile变量方式,将更容易编写和阅读。
此外,Volatile变量(不像锁)不会引起线程阻塞,因此它们不大可能造成可扩展性问题。
在读操作远远超过写操作情况下,Volatile变量可以获取对锁方式性能上的优势。
【正确使用Volatile变量的条件】
只有在有限的一些情况下,你才可以使用Volatile变量替换锁机制。Volatile变量提供线程安全需要满足以下两个条件:
1)变量写入值不依赖于它的当前值;2)变量不参与不等式判断(The variable does not participate in invariants with other variables);
这些条件指出可以写入到Volatile变量的数值是独立于任何其他程序状态的,包括变量的当前值。
第一个条件剥夺了Volatile变量作为线程安全计数器的资格。虽然自增操作(x++)看起来像个单一的操作,但是实际上它是一连串原子操作的组合(read-modify-write),而Volatile变量恰恰无法提供必要的原子性;
正确的操作需要x的值在自增操作期间保持不变,Volatile变量却无法实现这一点。(不过,如果你可以安排该值永远只从单个线程写入,那么可以忽略第一个条件。)
【正确使用Volatile变量的情景】
【Pattern #1: Status flags】
简单的布尔值状态标识是典型的Volatile变量应用,标识生命周期中重要的一次性事件的发生,比如初始化已完成或者关闭已请求;
许多应用程序包括以下形式的控制结构,"程序未终止前,持续工作",如图2所示:
1 2 3 4 5 6 7 | volatile boolean shutdownRequested; public void shutdown() { shutdownRequested = true ; } public void doWork() { while (!shutdownRequested) { // do stuff } } |
shutdown()将在循环外部被调用,可能在其它线程中,因此需要同步机制确保 shutdownRequested 变量的可见性。
在循环代码块中使用 synchronized 关键字相比于使用Volatile来修饰状态标识显得更加复杂与繁琐。由于Volatile变量简化了编码,并且该状态标识不依赖于程序中其它状态,
这是一个好的用例。
这种类型的状态标记具有一个共同特点:通常只存在一个状态转变。当shutdownRequested状态标识由false转变为true时,程序终止。
这种模式可以扩展到来回转变的状态标识,但也只能接受特定的转变周期(false-to-false),否则需要某种原子状态转变机制,例如原子变量。
【Pattern #2: one-time safe publication】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public class BackgroundFloobleLoader { public volatile Flooble theFlooble; public void initInBackground() { // do lots of stuff theFlooble = new Flooble(); // this is the only write to theFlooble } } public class SomeOtherClass { public void doWork() { while ( true ) { // do some stuff... // use the Flooble, but only if it is ready if (floobleLoader.theFlooble != null ) doSomething(floobleLoader.theFlooble); } } } |
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步