【发布时间】:2019-05-13 07:13:04
【问题描述】:
如果我有一个在线程之间共享的对象,在我看来,每个字段都应该是final 或volatile,原因如下:
如果该字段应该改变(指向另一个对象,更新原始值),那么该字段应该是
volatile,以便所有其他线程对新值进行操作。仅仅对访问所述字段的方法进行同步是不够的,因为它们可能会返回缓存值。如果该字段永远不会更改,请设置为
final。
但是,我找不到任何关于此的信息,所以我想知道这个逻辑是否有缺陷或过于明显?
EDIT当然可以使用final AtomicReference 或类似名称来代替易失性。
EDIT 例如,参见Is getter method an alternative to volatile in Java?
EDIT 避免混淆:这个问题是关于缓存失效的! 如果两个线程对同一个对象进行操作,对象的字段可以被缓存(每个线程) ,如果它们没有被声明为 volatile。如何保证缓存正确失效?
最终编辑感谢@Peter Lawrey,他向我指出了 JLS §17(Java 内存模型)。据我所知,它声明同步在操作之间建立了先发生关系,因此如果这些更新“发生在之前”,则线程可以看到来自另一个线程的更新,例如如果非易失性字段的 getter 和 setter 是 synchronized。
【问题讨论】:
-
Merely a synchronization on the methods which access said field is insufficient- 不,同步比volatile“更强”。这意味着如果另一个线程在其中,您甚至都不会进入该方法,因此您只能在另一个线程退出并完成更改值后进入。 -
问题是每个线程都可以有自己的缓存版本,所以线程1更新后线程2仍然可以看到旧版本。 tutorials.jenkov.com/java-concurrency/volatile.html
-
不,当正确使用
private字段时,所有访问都必须通过同步方法完成,最新值的可见性已经得到保证。正如@daniu 正确指出的那样,它甚至比声明变量volatile更强大。正如您链接的文章所指出的那样,volatile 并不总是足够的。 -
“易失性”是关于“缓存失效”的概念是错误的推理;它预设了一个特定的内存模型实现,即内存是用复制真实内存的处理器缓存实现的,或者变量是用缓存值的寄存器实现的。 Java 提供给您的抽象内存模型中正确性的原因,而不是它的任何特定实现。
-
至此:volatile 不是你撒在程序上的魔法尘埃,线程问题就会消失。易失性读取和写入仍然可能相互重新排序,这仍然会产生意外的竞争条件。 不要让易失性字段给您对程序正确性的不劳而获的安全感。同样,您需要在语言的内存模型的上下文中推理程序的正确性。
标签: java multithreading volatile final