【问题标题】:Synchronized in java ensuring memory updates for other threads?在java中同步确保其他线程的内存更新?
【发布时间】:2015-11-05 00:43:30
【问题描述】:

由于许多误解,我从头开始重新提出这个问题。问题的意图没有改变。许多 cmets 仍然参考旧的问题文本。

关于volatile 的文档指出,它确保其他线程以一致的方式看到内存更新。但是,volatile 很少使用。

据我所知,synchronized 块的目的是使线程不会同时执行这些关键部分。 synchronized 是否也会像 volatile 那样导致对其他线程进行一致的内存更新?

【问题讨论】:

  • Vector 已同步以防止您描述的内容。 (这是一个设计错误;同步速度很慢,通常是不必要的,并且在需要同步时经常不够用。)使用ArrayList,您会发现您的错误更频繁地困扰您。
  • 问题是:“显然,这不会发生,否则我在职业生涯中会遇到这样的问题。我从来没有遇到过。”
  • Vector 到处都是synchronized。 synchronized 提供了 volatile 行为的超集。
  • 是的,synchronized 块中的写入(甚至是非易失性变量)对于在写入后发生在同一个锁上同步的块中的相同变量的读取是可见的。
  • it ensures that modifications of two volatile fields by one thread will either be seen both by another threads, or none will be seen by the other thread 不,你绝对可以让第二个线程只看到两个 volatile 变量中的一个修改。

标签: java multithreading volatile


【解决方案1】:

当发生在锁定部分内时,Java 中的线程之间的所有内存都会持续更新,这真的是真的吗?

简答:
是的。

长答案:

在 JVM 中,引用类型和除 double 和 long 之外的所有原始类型的分配是“原子的”。 IE。任何线程要么看到一个值,要么看到另一个值,但永远不会看到一半更新的值。所以你甚至可以说内存是“一致地”更新而不做任何事情(这有一些微妙之处,所以不要试图实际使用这个特性!它被用于实现String#hashCode)。

但是,如果没有volatile,更改可能不会像您说的那样立即可见(“立即”有点误导。有关更多详细信息,请参阅this answer)。 synchronized 关键字确实以与 volatile 相同的方式使更改立即可见。那么你问volatile 的意义何在。

基本上你可以做任何volatile 可以使用synchronized 做的事情。所以volatile 并没有给你新的东西你可以做。但是,它仍然有用,因为它避免了锁定。 volatile 的性能通常更好(权衡是更少的功能;synchronized 可以做所有volatile 可以做的事情,但反之则不行)并且也没有死锁风险。 IMO 如果您可以使用volatile,您应该使用volatile。但是有很多人不同意,认为你应该在任何地方都使用synchronized。

【讨论】:

  • 所以你的意思是在同步块中发生的任何内存更改都会在所有其他线程进入同一个互斥对象上的另一个同步块时立即自动对所有其他线程可见,对吗?
  • @DanielS。如果它在同一个对象上同步,是的(一次只有一个线程可以获得锁,所以当一个线程在块中执行时,没有其他线程)
  • volatile 不保证更改“立即”可见。它保证了一致性,但只要延迟不违反任何发生前的约束,您仍然可以在 volatile 写入传播时获得任意延迟。
  • @user2357112:是的......我只是想我不会进入那个,但我会编辑我的答案。
  • @DanielS.:我在这里的其他答案也可能有帮助stackoverflow.com/a/15654830/234901
【解决方案2】:

Java 并没有真正的“易失性内存”概念。相反,volatile 关键字改变了 JVM 关于何时以及如何写入和读取字段的保证。 Wikipedia 对 volatile 的 Java(和其他语言)的含义进行了很好的细分。

非常粗略地说,volatile 字段等价于始终读取和写入的字段,如下所示:

// read
T local;
synchronized {
  local = field;
}

// ... do something with local

// write
synchronized {
  field = local;
}

换句话说,volatile 字段的读取和写入是原子的,并且始终对其他线程可见。对于简单的并发操作,这已经足够了,但您可能仍需要显式同步以确保正确处理复合操作。 volatile 的优势在于它更小(仅影响一个字段),因此可以比同步代码块更有效地处理。

您会从我的伪翻译中注意到,对该字段的任何修改或突变都不是synchronized,这意味着您可能对检索到的对象进行的任何操作都不会施加内存障碍或发生前的关系。 唯一 volatile 提供的是线程安全字段读取和写入。这是一项功能,因为当您只需要更新字段时,它比 synchronized 块更有效。

如果您以前没有阅读过Java Concurrency in Practice,我强烈建议您阅读;它很好地概述了您需要了解的有关 Java 并发性的所有信息,包括 volatile 和 synchronized 之间的区别。


显然,这不会发生,否则我在职业生涯中会遇到这样的问题。我从来没有。

请注意,这是一种错误的推理方式。你完全有可能做错事,但永远不会受到它的影响,或者根本没有注意到它的影响。例如,过度同步的代码(如Vector)可以正常工作,但运行速度比其他解决方案慢得多。

【讨论】:

  • 1) volatile 不仅仅具有在 32 位处理器上一致地读/写单个长字段的效果。 2)我很清楚这种推理谬误,但是看看我的例子:如果 Vector 有一个严重的线程安全错误,那么人们会注意到它的概率如此之高,以至于你可以将其视为你生活中的确定性。不是数学上的,而是实际的。
  • 不太正确:volatile 与监视器(synchronized 关键字)具有相同的效果(同步)。两者都使同步写入和所有以前的写入对获取相同变量的线程可见。参考文献Java 并发实践(说真的,这是一本非常好的书)。 Volatile 不是“一个单一的长字段”,它比这要大得多。
  • @DanielS。 1)我不知道你的意思 - 也许你可以链接到引文?就像我说的,我上面的代码 sn-p 只是大致发生了什么,但它确实反映了同步方面的直觉和记忆效应。
  • 2) Vector 是线程安全的,因为它被设计和记录为线程安全的,而不是因为您从未遇到过它的问题。这就是谬误。尽管如此,synchronized 并不是确保线程安全的唯一方法,而现代代码中Volatile is not recommended 的真正原因是因为它使用synchronized 来提供线程安全,而不是其他更好的模式。
  • 嘿,当然,与其说“我从来没有遇到过 Vector 的问题”,不如写成“我们都同意 Vector 有效”。尽管如此,它的设计和记录是正确的并不能证明它是正确的。那里也一样。看看我想说什么,而不是试图误解我的意思。
【解决方案3】:

当然,使用临界区是可行的。使用 volatile 作品。但是您必须使用某些东西来确保获得所需的内存可见性。否则,您的代码只能靠运气或意外工作——如果它有效的话。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多