【问题标题】:Can using volatile with "AtomicInteger" guarantees thread safety? [duplicate]将 volatile 与“AtomicInteger”一起使用可以保证线程安全吗? [复制]
【发布时间】:2018-02-15 19:21:19
【问题描述】:

假设我有

private volatile AtomicInteger atomInt = new AtomicInteger(3);

在方法中我的用法是atomInt.incrementAndGet()

由于我使用的是AtomicInteger,所以会避免“线程干扰”。然后我使用 volatile,因此它将保证所有线程中变量的一致视图。这是否意味着我已经获得了完整的线程安全性,或者仍然存在“内存一致性问题”的可能性?

由于tutorial 中使用了“减少”,我感到困惑,所以这表明我仍有机会,但我想不到:

使用易失变量降低内存一致性的风险 错误,因为对 volatile 变量的任何写入都会建立 与后续读取相同的发生之前的关系 变量。

【问题讨论】:

  • 您是否曾经重新分配atomInt(如atomInt = ...)?如果不是,则使其成为最终的,而不是易变的。 volatile 是指变量的值,而不是在变量指向的东西中声明。
  • @AndyTurner 感谢您抽出宝贵时间回复。好的,我认为这样可以保证线程安全,对吧?但我仍在寻找我的问题的答案。
  • 您的问题的答案是,如果您从不重新分配变量,则不需要使用 volatile,如果需要,您可能需要使用它。
  • 同上@AndyTurner。将变量声明为final,而不是volatile。如果您使用的是 AtomicInteger 变量,而您不能将其声明为 final,那么您要么在做一些非常棘手的事情,要么更有可能是出了点问题。
  • Re,“这是否意味着我已经获得了完整的线程安全......?”完全线程安全是指保证您的多线程程序始终做正确的事情。你还没有向我们展示一个完整的程序,所以没有办法真正回答这个问题。线程安全是整个程序架构的一个属性。这不是你可以一次一行地赋予程序的东西。

标签: java multithreading atomic volatile


【解决方案1】:

然后我使用volatile,因此它将保证变量在所有线程中的一致视图。

原子变量已经保证了线程安全。如果您不重新分配变量,volatile 是多余的。您可以在此处将volatile 替换为final

private final AtomicInteger atomInt = new AtomicInteger(3);

这是否意味着我已经获得了完全的线程安全性,还是仍然存在“内存一致性问题”的可能性?

目前,它绝对是线程安全的。 变量不会发生“内存一致性问题”。但是使用适当的线程安全组件并不意味着整个类/程序都是线程安全的。如果它们之间的交互不正确,可能会出现问题。

使用 volatile 变量可降低内存一致性错误的风险 ...

volatile 变量只能保证可见性。他们不保证atomicity

正如Brian Goetz 所写(强调我的):

volatile 变量很方便,但它们有局限性。 volatile 变量最常见的用途是作为完成、中断或状态标志。易失性变量可用于其他类型的状态信息,但在尝试此操作时需要更加小心。例如,volatile 的语义不足以使增量操作 (count++) 成为原子操作,除非您可以保证该变量仅由单个线程写入。

只有在满足以下所有条件时才能使用 volatile 变量:

  • 对变量的写入不依赖于其当前值,或者您可以确保只有单个线程更新该值;
  • 该变量与其他状态变量不参与不变量;
  • 在访问变量时,出于任何其他原因不需要锁定。

来自java.util.concurrent.atomic 包的文档:

  • get 具有读取 volatile 变量的记忆效应。
  • set 具有写入(分配)volatile 变量的记忆效应。

【讨论】:

  • 但是我在我的问题中提供的 Java 教程中的这个呢?“原子操作不能交错,因此可以使用它们而不必担心线程干扰。但是,这并不能消除所有需要同步原子动作,因为仍然可能出现内存一致性错误。"
  • 我对“java.util.concurrent.atomic”包的描述有点困惑,因为不使用volatile或synchronized,“read barrier”和“write barrier”将不会执行,如果它们不执行,则无法保证将缓存值刷新到主内存。
  • @pjj 原子访问和更新的内存效应通常遵循 volatile 的规则,如 Java 语言规范(17.4 内存模型)中所述@指出Andrew Tobilko 但是如果你需要原子地改变几个Atomics 或者使用 check-then-act 那么你需要额外的同步
【解决方案2】:

易失性确实意味着对变量的更改将是可见的。但在这种情况下,您不应该更改变量持有的引用。

你想引用一个 volatile 的 Atomic 对象似乎很奇怪。 atomicinteger 类的重点是提供一种安全访问和更改整数值的方法。使某些变量 volatile 的唯一原因是因为您打算覆盖它的值。当您可以使用 AtomicInteger 的实例方法更新其值时,为什么要覆盖对 AtomicInteger 的引用?

这就是为什么你会得到建议让这个变量成为 final 而不是 volatile。使变量最终确定引用,使其不能更改,同时确保该变量包含的引用是可见的。 atomicInteger 以线程安全的方式管理自己的状态,因此您不必覆盖对它的引用。

所以在这里说 volatile 是多余的并不完全正确。但它正在做一些通常不应该做的事情。当您必须更改变量中包含的值时,请使用 volatile。当您不应该更改变量中包含的值时,请使用 final。

【讨论】:

    猜你喜欢
    • 2016-10-28
    • 2021-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-09
    • 1970-01-01
    • 1970-01-01
    • 2019-04-01
    相关资源
    最近更新 更多