更喜欢 synchronized 而不是 volatile

Preferring synchronized to volatile

我读过这个answer,最后写了以下内容:

Anything that you can with volatile can be done with synchronized, but not vice versa.

不清楚。 JLS 8.3.1.4 定义可变字段如下:

A field may be declared volatile, in which case the Java Memory Model ensures that all threads see a consistent value for the variable (§17.4).

所以,volatile 字段是关于内存可见性的。另外,就我引用的答案而言,读取和写入易失性字段是同步的。

同步反过来保证只有一个线程可以访问同步块。据我了解,它与内存可见性无关。我错过了什么?

错了。同步与内存可见性有关。每个线程都有自己的缓存。如果你有一个锁,缓存是refresehd。如果你释放一个锁,缓存就会被写入主内存。

如果你读一个volatile字段也有一个刷新,如果你写一个volatile字段有一个flush。

事实上,同步也与内存可见性有关,因为 JVM 在同步块的出口添加了一个 内存屏障。这确保同步块中线程的写入结果保证对另一个线程的读取可见一旦第一个线程退出同步块。

注意: 按照@PaŭloEbermann 的评论,如果另一个线程通过读取内存屏障(例如通过进入同步块),它们的本地缓存将不会失效因此他们可能会读取旧值。

同步块的退出是 happens-before 在此文档中:http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility

寻找这些摘录:

The results of a write by one thread are guaranteed to be visible to a read by another thread only if the write operation happens-before the read operation.

An unlock (synchronized block or method exit) of a monitor happens-before every subsequent lock (synchronized block or method entry) of that same monitor. And because the happens-before relation is transitive, all actions of a thread prior to unlocking happen-before all actions subsequent to any thread locking that monitor.

synchronized 和 volatile 是不同的,但通常它们都用于解决同一个常见问题。

同步是为了确保在给定的时间点只有一个线程会访问共享资源。

然而,那些共享资源通常被声明为易变的,这是因为,如果一个线程改变了共享资源值,它也必须在另一个线程中更新。但是如果没有 volatile,运行时只会通过从缓存中读取值来优化代码。所以 volatile 的作用是,每当任何线程访问 volatile 时,它​​不会从缓存中读取值,而是实际上从实际内存中获取它并使用它。


正在查看 log4j 代码,这是我发现的。

/**
 * Config should be consistent across threads.
 */
protected volatile PrivateConfig config;

如果多个线程写入一个共享的 volatile 变量并且它们还需要使用它的先前值,它可以创建一个 race condition。所以这时候就需要使用同步了。

... if two threads are both reading and writing to a shared variable, then using the volatile keyword for that is not enough. You need to use a synchronized in that case to guarantee that the reading and writing of the variable is atomic. Reading or writing a volatile variable does not block threads reading or writing. For this to happen you must use the synchronized keyword around critical sections.

关于volatile的详细教程见'volatile' is not always enough.