【问题标题】:Is it possible to use visibility guarantees instead of full-fledged locking for accessing shared state in a thread-safe way?是否可以使用可见性保证而不是完整的锁定来以线程安全的方式访问共享状态?
【发布时间】:2019-10-18 16:54:34
【问题描述】:

Java Concurrency in Practice 陈述如下:

当线程 A 执行一个同步块,随后线程 B 进入由同一个锁保护的同步块,值 释放锁之前对 A 可见的变量是 保证在获得锁时对 B 可见。换一种说法, A 同步块之前所做的一切对 B 可见,当 它执行由同一个锁保护的同步块。 没有同步,就没有这样的保证。

同样的逻辑也适用于 volatile 变量:

volatile 变量的可见性影响超出了值 volatile 变量本身。当线程 A 写入 volatile 变量,随后线程 B 读取相同的变量, 在写入之前对 A 可见的所有变量的值 volatile 变量在读取 volatile 后对 B 可见 变量。

从描述中可以非常清楚地看到,当您需要访问某些共享状态时,您可以使用这种可见性效果来实际替换(或限制使用)传统锁。在图中的示例中,您可以看到线程 B 可以安全地读取变量 y,即使它在同步块之外的线程 A 中被更改。

那么,当您在锁定线程之前更改某些共享状态,然后获取锁定,做某事(或者什么都不做,我猜),释放锁定,然后在其他线程你获得相同的锁,释放它,然后安全地从第一个线程中更新的共享变量中读取最新值?

【问题讨论】:

  • 同步确保线程 A 将在线程 B 进入之前完成该块。而volatile 只会在线程 A 和 B 不同时读取/写入成员时生效。例如。当线程 A 和 B 同时写入成员 m 时,您无法保证将写入和读取哪个值。线程 C 收到的值几乎是不确定的(你根本无法确定)
  • 我不太确定您要回答问题的哪一部分。我的问题主要是关于释放锁时 JVM 提供的可见性保证。在我提到的示例图中,您可以看到 y 变量在同步块之外发生了更改,但仍然可以从线程 B 安全地访问,该线程 B 在释放锁后访问该变量。
  • 所以你基本上会问这本书是不是在骗你?您有什么理由不信任它而信任我们?
  • 我基本上是在问为什么我应该使用同步块(或 java.util.concurrent.locks 包中的一些锁对象)并将任何处理共享状态的代码放入其中,当我可以使用书中描述的技术并尽可能限制锁/同步块的使用(例如,不包括其中的任何内容),因为reader 线程无论如何都可以获得最近的更改?
  • @J.Doe 我准确地回答了你最后的评论。当您不使用 synchronization 时,您无法确定您的 reader 线程是否遇到了有效值,也许它在另一个线程正在写入的同时从字段中读取值。

标签: java multithreading concurrency


【解决方案1】:

是的,您可以通过使用volatilesynchronized 或任何一种锁定形式以上述方式获得visibility guarantee,但仅此而已。具体来说,您不会获得相互访问和原子性。

另请参阅 JLS 中有关 JVM 的 memory modelhappens-before 关系的部分,以获得更多详细信息和深度。

【讨论】:

  • 是否可以肯定地说,以下操作将保证两个线程之间某些共享状态更改的可见性,例如: 1 线程 - 更改某些共享状态,获取锁,释放锁; 2 线程 - 获取锁、释放锁、访问共享状态?
  • 从纯粹的可见性 POV 是的。但是,您仍然必须通过其他方式确保线程 1 在线程 2 之前对锁起作用。
  • 但是对于 2 线程来说,获取/释放锁然后访问不一定被 1 线程更改的共享状态就足够了,对吗?
【解决方案2】:

引用句中有一个关键部分:

volatile 变量的可见性影响超出了值 volatile 变量本身。 当线程 A 写入 volatile 变量,随后线程 B 读取相同的变量, 在写入之前对 A 可见的所有变量的值 volatile 变量在读取 volatile 后对 B 可见 变量。

虽然这显然是正确的,但您在这里得到了一个条件,即 A 在 B 阅读之前写作。 如果没有锁定,则无法保证读/写顺序 - 这就是重点。

因此,您不能用 volatile 替换每个同步,因为它不会产生相同的结果。

【讨论】:

  • 恐怕你漏掉了这句话all variables that were visible to A prior to writing to the volatile variable become visible to B after reading the volatile variable.
  • 我在这里缺少什么?您只是无法保证 A 中的写入将在 B 尝试读取之前进行。所以它不是关于“可见性”,而是关于执行顺序的保证。我认为你误解了那句话。
  • 这部分说你基本上可以在一个线程中改变一些共享状态,然后写入一个 volatile 变量。在第二个线程中,您首先从该 volatile 变量中读取并访问在第一个线程中更改的共享状态。并且保证您将获得此状态的最新更改。
  • 但这仅适用于给定的执行顺序 - 这不是保证。显然 READ after WRITE 会产生预期的结果,但首先你必须确保执行顺序是持续的。
  • 所以你说的是写后读会给你“改变”。我的意思是,你不知道你会在写后真正阅读。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-10
  • 1970-01-01
  • 2019-12-25
  • 2013-01-22
  • 2015-02-10
相关资源
最近更新 更多