我读过这个 回答 最后写了以下内容:
你可以用volatile来完成任何事情,但是
反之亦然。
目前尚不清楚。 JLS 8.3.1.4 定义volatile字段如下:
字段可以声明为volatile,在这种情况下是Java Memory Model
确保所有线程都看到变量的一致值
(§17.4)。
因此,volatile字段与内存可见性有关。另外,据我所提到的答案,读取和写入易失性字段是同步的。
同步反过来保证只有一个线程可以访问同步块。正如我所知,它与内存可见性无关。我错过了什么?
事实上,同步也与内存可见性有关,因为JVM增加了一个 记忆障碍 在synchronized块的出口处。这确保了同步块中线程的写入结果可以保证通过另一个线程的读取可见 一旦 第一个线程已退出synchronized块。
注意 : 按照@PaŭloEbermann的评论,如果另一个线程通过读取内存屏障(例如,通过进入同步块),它们的本地缓存将不会失效,因此它们可能会读取旧值。
同步块的退出是a 之前发生 在这个文档中: http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility
寻找这些提取物:
一个线程写入的结果保证对a可见
只有在写入操作发生之前,才由另一个线程读取
读操作。
和
监视器的解锁(同步块或方法退出)
发生在每个后续锁定之前(同步块或方法)
那个监视器的入口)。而且因为发生在之前的关系
是传递的,解锁之前线程的所有动作
发生 - 在任何线程锁定之后的所有操作之前发生
监控。
同步和易失性是不同的,但通常它们都用于解决相同的常见问题。
Synchronized是为了确保在给定的时间点只有一个线程将访问共享资源。
然而,这些共享资源通常被声明为volatile,这是因为,如果一个线程已经改变了共享资源值,它也必须在另一个线程中更新。但是没有volatile,运行时只需通过从缓存中读取值来优化代码。所以volatile的作用是,每当任何线程访问volatile时,它都不会从缓存中读取值,而是实际从实际内存中获取它并使用相同的内容。
正在浏览log4j代码,这就是我发现的。
/**
* Config should be consistent across threads.
*/
protected volatile PrivateConfig config;
那是错的。同步与内存可见性有关。每个线程都有自己的缓存。如果你有一个锁,缓存就是refresehd。如果释放锁定,则缓存将延伸到主存储器。
如果你读一个volatile字段也有刷新,如果你写一个volatile字段就有一个刷新。
如果多个线程写入共享的volatile变量并且它们还需要使用它的先前值,则它可以创建一个 竞争条件。所以此时你需要使用同步。
...如果两个线程都在读取和写入共享变量,那么使用volatile关键字是不够的。在这种情况下,您需要使用synchronized来保证变量的读取和写入是原子的。读取或写入volatile变量不会阻止线程读取或写入。为此,您必须在关键部分周围使用synchronized关键字。
有关volatile的详细教程,请参阅 “挥发性”并不总是足够的。