内容摘要:本文介绍了几种有效使用 volatile 变量模式,并强调了几种不适合使用 volatile 变量情形。 锁提供了两种主要特性:互斥(mutual exclusion) 和可见性(visibility)。互斥即一次只允许一个线程持有某个特定锁,因此可使用该特性实现对共享数据协调访问协议,这样,一次就只有一个线程能够使用该共享数据。可见性要更加复杂一些,它必须确保释放锁之前对共享数据做出更改对于随后获得该锁另一个线程是可见 —— 如果没有同步机制提供这种可见性保证,线程看到共享变量可能是修改前值或不一致值,这将引发许多严重问题。 字串8 Volatile 变量 字串8 Volatile 变量具有 synchronized 可见性特性,但是不具备原子特性。这就是说线程能够自动发现 volatile 变量最新值。Volatile 变量可用于提供线程安全,但是只能应用于非常有限一组用例:多个变量之间或者某个变量当前值与修改后值之间没有约束。因此,单独使用 volatile 还不足以实现计数器、互斥锁或任何具有与多个变量相关不变式(Invariants)类(例如 “start <=end”)。 字串3 出于简易性或可伸缩性考虑,您可能倾向于使用 volatile 变量而不是锁。当使用 volatile 变量而非锁时,某些习惯用法(idiom)更加易于编码和阅读。此外,volatile 变量不会像锁那样造成线程阻塞,因此也很少造成可伸缩性问题。在某些情况下,如果读操作远远大于写操作,volatile 变量还可以提供优于锁性能优势。 字串6
内容摘要:本文介绍了几种有效使用 volatile 变量模式,并强调了几种不适合使用 volatile 变量情形。 您只能在有限一些情形下使用 volatile 变量替代锁。要使 volatile 变量提供理想线程安全,必须同时满足下面两个条件: 字串3 对变量写操作不依赖于当前值。 字串1 该变量没有包含在具有其他变量不变式中。 字串4
字串3
字串2 大多数编程情形都会与这两个条件其中之一冲突,使得 volatile 变量不能像 synchronized 那样普遍适用于实现线程安全。清单 1 显示了一个非线程安全数值范围类。它包含了一个不变式 —— 下界总是小于或等于上界。 字串9
@NotThreadSafe
内容摘要:本文介绍了几种有效使用 volatile 变量模式,并强调了几种不适合使用 volatile 变量情形。 性能考虑 字串7 使用 volatile 变量主要原因是其简易性:在某些情形下,使用 volatile 变量要比使用相应锁简单得多。使用 volatile 变量次要原因是其性能:某些情况下,volatile 变量同步机制性能要优于锁。 字串7 很难做出准确、全面评价,例如 “X 总是比 Y 快”,尤其是对 JVM 内在操作而言。(例如,某些情况下 VM 也许能够完全删除锁机制,这使得我们难以抽象地比较 volatile 和 synchronized 开销。)就是说,在目前大多数处理器架构上,volatile 读操作开销非常低 —— 几乎和非 volatile 读操作一样。而 volatile 写操作开销要比非 volatile 写操作多很多,因为要保证可见性需要实现内存界定(Memory Fence),即便如此,volatile 总开销仍然要比锁获取低。 字串5 volatile 操作不会像锁一样造成阻塞,因此,在能够安全使用 volatile 情况下,volatile 可以提供一些优于锁可伸缩特性。如果读操作次数要远远超过写操作,与锁相比,volatile 变量通常能够减少同步性能开销。 字串6
字串5 内容摘要:本文介绍了几种有效使用 volatile 变量模式,并强调了几种不适合使用 volatile 变量情形。 很多并发性专家事实上往往引导用户远离 volatile 变量,因为使用它们要比使用锁更加容易出错。然而,如果谨慎地遵循一些良定义模式,就能够在很多场合内安全地使用 volatile 变量。要始终牢记使用 volatile 限制 —— 只有在状态真正独立于程序内其他内容时才能使用 volatile —— 这条规则能够避免将这些模式扩展到不安全用例。 字串6 模式 #1:状态标志 字串2 也许实现 volatile 变量规范使用仅仅是使用一个布尔状态标志,用于指示发生了一个重要一次性事件,例如完成初始化或请求停机。 字串7
字串8 清单 2. 将 volatile 变量作为状态标志使用 字串4
这种类型状态标记一个公共特性是:通常只有一种状态转换;shutdownRequested 标志从 false 转换为 true,然后程序停止。这种模式可以扩展到来回转换状态标志,但是只有在转换周期不被察觉情况下才能扩展(从 false 到 true,再转换到 false)。此外,还需要某些原子状态转换机制,例如原子变量。 字串6
内容摘要:本文介绍了几种有效使用 volatile 变量模式,并强调了几种不适合使用 volatile 变量情形。 缺乏同步会导致无法实现可见性,这使得确定何时写入对象引用而不是原语值变得更加困难。在缺乏同步情况下,可能会遇到某个对象引用更新值(由另一个线程写入)和该对象状态旧值同时存在。(这就是造成著名双重检查锁定(double-checked-locking)问题根源,其中对象引用在没有同步情况下进行读操作,产生问题是您可能会看到一个更新引用,但是仍然会通过该引用看到不完全构造对象)。 字串2 实现安全发布对象一种技术就是将对象引用定义为 volatile 类型。清单 3 展示了一个示例,其中后台线程在启动阶段从数据库加载一些数据。其他代码在能够利用这些数据时,在使用之前将检查这些数据是否曾经发布过。 字串7 清单 3. 将 volatile 变量用于一次性安全发布 字串4 public class BackgroundFloobleLoader { 如果 theFlooble 引用不是 volatile 类型,doWork() 中代码在解除对 theFlooble 引用时,将会得到一个不完全构造 Flooble。 字串8 该模式一个必要条件是:被发布对象必须是线程安全,或者是有效不可变对象(有效不可变意味着对象状态在发布之后永远不会被修改)。volatile 类型引用可以确保对象发布形式可见性,但是如果对象状态在发布后将发生更改,那么就需要额外同步。 字串8
字串3 内容摘要:本文介绍了几种有效使用 volatile 变量模式,并强调了几种不适合使用 volatile 变量情形。 字串5 安全使用 volatile 另一种简单模式是:定期 “发布” 观察结果供程序内部使用。例如,假设有一种环境传感器能够感觉环境温度。一个后台线程可能会每隔几秒读取一次该传感器,并更新包含当前文档 volatile 变量。然后,其他线程可以读取这个变量,从而随时能够看到最新温度值。 字串4 使用该模式另一种应用程序就是收集程序统计信息。清单 4 展示了身份验证机制如何记忆最近一次登录用户名字。将反复使用 lastUser 引用来发布值,以供程序其他部分使用。 字串3 清单 4. 将 volatile 变量用于多个独立观察结果发布 字串5 public class UserManager { 该模式是前面模式扩展;将某个值发布以在程序内其他地方使用,但是与一次性事件发布不同,这是一系列独立事件。这个模式要求被发布值是有效不可变 —— 即值状态在发布后不会更改。使用该值代码需要清楚该值可能随时发生变化。 字串8 模式 #4:“volatile bean” 模式 字串8
字串2 内容摘要:本文介绍了几种有效使用 volatile 变量模式,并强调了几种不适合使用 volatile 变量情形。 字串9 清单 5. 遵守 volatile bean 模式 Person 对象 字串4 @ThreadSafe volatile 高级模式 字串3 前面几节介绍模式涵盖了大部分基本用例,在这些模式中使用 volatile 非常有用并且简单。这一节将介绍一种更加高级模式,在该模式中,volatile 将提供性能或可伸缩性优势。 字串4 volatile 应用高级模式非常脆弱。因此,必须对假设条件仔细证明,并且这些模式被严格地封装了起来,因为即使非常小更改也会损?img alt="" src="http://51dev.com/ads/de.gif" />?img alt="" src="http://51dev.com/ads/de.gif" />代码!同样,使用更高级 volatile 用例原因是它能够提升性能,确保在开始应用高级模式之前,真正确定需要实现这种性能获益。需要对这些模式进行权衡,放弃可读性或可维护性来换取可能性能收益 —— 如果您不需要提升性能(或者不能够通过一个严格测试程序证明您需要它),那么这很可能是一次糟糕交易,因为您很可能会得不偿失,换来东西要比放弃东西价值更低。 字串4
字串2 内容摘要:本文介绍了几种有效使用 volatile 变量模式,并强调了几种不适合使用 volatile 变量情形。 字串1
然而,如果读操作远远超过写操作,您可以结合使用内部锁和 volatile 变量来减少公共代码路径开销。清单 6 中显示线程安全计数器使用 synchronized 确保增量操作是原子,并使用 volatile 保证当前结果可见性。如果更新不频繁话,该方法可实现更性能,因为读路径开销仅仅涉及 volatile 读操作,这通常要优于一个无竞争锁获取开销。 字串3
字串6 @ThreadSafe
字串1 结束语 字串1 与锁相比,Volatile 变量是一种非常简单但同时又非常脆弱同步机制,它在某些情况下将提供优于锁性能和伸缩性。如果严格遵循 volatile 使用条件 —— 即变量真正独立于其他变量和自己以前值 —— 在某些情况下可以使用 volatile 代替 synchronized 来简化代码。然而,使用 volatile 代码往往比使用锁代码更加容易出错。本文介绍模式涵盖了可以使用 volatile 代替 synchronized 最常见一些用例。遵循这些模式(注意使用时不要超过各自限制)可以帮助您安全地实现大多数用例,使用 volatile 变量获得更佳性能。 字串7 字串4 |
java Volatile变量
最新推荐文章于 2019-02-18 18:24:00 发布