【问题标题】:How can read-modify-write operations of volatile variables be thread safevolatile变量的读-修改-写操作如何是线程安全的
【发布时间】:2017-12-19 02:58:09
【问题描述】:

我正在阅读 JCIP,无法理解 3.3.1 中的以下语句,

只要您可以确保 volatile 变量仅从 SINGLE 线程写入,对共享 volatile 变量执行读-修改-写操作是安全的。在这种情况下,您将修改限制在单个线程以防止出现竞争条件,并且 volatile 变量的可见性保证可确保其他线程看到最新的值。

即使 volatile 变量只从单个线程写入,它怎么能不引发竞态条件呢?如果线程 A 在 counter 为 1 时执行 counter++,则线程 B 可以在 counter+1 写回内存之前进入,并获得 counter 的陈旧值 1。如何确保“防止竞争条件”和“其他线程看到最新的值”??

附言我知道here 有同样的问题,但我没有找到任何令人满意的答案。

【问题讨论】:

  • “线程 B 可以在 counter+1 写回内存之前进入”。是的——这意味着线程 B 在读取数据时获得了最新的值。这就是它所确保的。在您看来,这如何造成竞争条件?
  • 规范尽可能清晰。在您的示例中,包括线程 B 在内的所有线程都将在 counter+1 写入内存之前看到 1 。一旦写入,他们都会看到 2。绝不会有一些线程看到 1 而其他线程看到 2。
  • @tsolakp 你是对的。我对“陈旧价值”的概念感到困惑。这里线程 B 读取的是“更新”值,而不是“陈旧”值,因为那是 A 最后写入内存的值。线程 A 和 B 在这里完美同步。
  • @Erwin 阅读您所说的内容后,这很有意义。谢谢。

标签: java multithreading


【解决方案1】:

易失性变量的读-修改-写操作如何实现线程安全

一般来说,他们不能。

你引用的文字说:

“对共享的 volatile 变量执行读-修改-写操作是安全的,只要您可以确保 volatile 变量仅从单个线程写入。” em>

这不是线程安全的意思。作为一般性声明。

即使 volatile 变量只从单个线程写入,它怎么能不引发竞态条件呢?

嗯,读取线程没有竞争条件,因为它们只会看到最近写入的变量值。并且写入线程受正常的执行规则支配,因此线程与自身之间不能存在竞争条件。

如何确保“防止竞争条件”?

见上文。如果有多个线程更新,您只能参加比赛。从竞态条件的定义来看是不言而喻的。

[和]“其他线程看到最新的值”?

这是 Java 内存模型对volatile 指定的结果。对 volatile 变量的读取保证可以看到任何线程最近一次写入的值……。

如果线程 A 在 counter 为 1 时执行 counter++,线程 B 可以在 counter+1 被写回内存之前进入,并获得一个陈旧的 counter 值 1。

在那种情况下,没有竞争条件。线程 B 正在查看最近写入的值 counter。计数器尚未更新。 这意味着count++ 不是原子的。但是线程安全和原子并不意味着同样的事情。原子是更强大的保证。并且 JLS 明确指出 count++ 对于 volatile 不是原子的。

现在(显然)如果您的应用程序有 多个 共享的 volatile 变量由不同的(单个)线程更新,那么前面段落的简单推理可能会失效。

【讨论】:

  • 只是好奇,如果使用单一写入策略,使用原子lazySet 更新状态是否安全?此外,与单写策略保持一致的“感知”原子性与线程安全之间有什么区别吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-05
  • 1970-01-01
  • 2017-05-08
  • 2021-03-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多