Java:使所有字段都是final还是volatile?
如果我有一个在线程之间共享的对象,那么在我看来,每个字段都应该是Java:使所有字段都是final还是volatile?,java,multithreading,volatile,final,Java,Multithreading,Volatile,Final,如果我有一个在线程之间共享的对象,那么在我看来,每个字段都应该是final或volatile,理由如下: 如果该字段应该更改(指向另一个对象,更新原语值),那么该字段应该是易变的,以便所有其他线程都对新值进行操作。仅仅对访问所述字段的方法进行同步是不够的,因为它们可能返回缓存的值 如果该字段永远不应更改,则将其设置为final 然而,我找不到关于这一点的任何东西,所以我想知道这一逻辑是否有缺陷或太明显了 编辑当然,可以使用最终原子引用或类似引用来代替volatile 编辑例如,请参见 编辑以
final
或volatile
,理由如下:
- 如果该字段应该更改(指向另一个对象,更新原语值),那么该字段应该是易变的,以便所有其他线程都对新值进行操作。仅仅对访问所述字段的方法进行同步是不够的,因为它们可能返回缓存的值
- 如果该字段永远不应更改,则将其设置为
final
最终原子引用
或类似引用来代替volatile
编辑例如,请参见
编辑以避免混淆:这个问题是关于缓存失效的如果两个线程在同一个对象上运行,那么如果对象的字段未声明为volatile,则可以缓存(每个线程)。如何保证缓存正确失效
最终编辑感谢@Peter Lawrey,他为我指出了JLS§17(Java内存模型)。据我所见,它指出同步在操作之间建立了一个“发生在之前”的关系,这样一个线程就可以看到来自另一个线程的更新,如果这些更新“发生在之前”,例如,如果非易失性字段的getter和setter是
同步的
则无论线程问题如何,创建不需要更改的final
字段都是一个好主意。它使类的实例更容易推理,因为您可以更容易地知道它处于什么状态
在使其他字段不稳定方面
:
仅仅对访问所述字段的方法进行同步是不够的,因为它们可能返回缓存的值
只有在访问同步块外部的值时,才会看到缓存的值
所有访问都需要正确同步。一个同步块的结束保证发生在另一个同步块的开始之前(在同一监视器上同步时)
至少有两种情况仍然需要使用同步:
- 如果必须以原子方式读取并更新一个或多个字段,则需要使用同步。
- 您可以避免某些单字段更新的同步,例如,如果您可以使用
类而不是“普通旧字段”;但是,即使是单个字段更新,您也可能需要独占访问(例如,将一个元素添加到列表中,同时删除另一个元素)原子*
- 您可以避免某些单字段更新的同步,例如,如果您可以使用
- 此外,volatile/final可能不足以用于非线程安全的值,例如
或数组ArrayList
private final
应该是字段和变量的默认值,带有var
这样的关键字,使其可变,在不需要时使用volatile
- 慢得多,通常慢10倍左右
- 通常不会为您提供所需的线程安全性,但会降低这些bug出现的可能性,从而使查找这些bug变得更加困难
- 不像
那样,final通过说这不应该被更改来提高清晰度,在不需要时使用final
,在读者试图弄清楚它为什么被设置为volatile时可能会令人困惑volatile
volatile int x;
x++;
这不是线程安全的。因为它和
int x2 = x;
x2 = x2 + 1; // multiple threads could be executing on the same value at this point.
x = x2;
更糟糕的是,使用volatile
会使这种bug更难发现
正如yshavit所指出的,使用volatile
更新多个字段比较困难,例如HashMap.put(a,b)
更新多个引用
仅仅对访问所述字段的方法进行同步是不够的,因为它们可能返回缓存的值
synchronized为您提供了volatile
和更多的内存保证,这就是它速度显著较慢的原因
注意:仅仅同步每个方法并不总是足够的StringBuffer
使每个方法都同步,但在多线程上下文中最糟糕的莫过于无用,因为它的使用可能容易出错
人们很容易认为实现线程安全就像撒上仙尘,加上一些神奇的线程安全,你的bug就会消失。问题是螺纹安全更像是一个有许多孔的桶。塞住最大的漏洞,bug可能会消失,但除非你把它们全部塞住,否则你就没有线程安全性,但这可能更难找到
就同步与易失性而言,这表明
其他机制,如易变变量的读写和java.util.concurrent包中类的使用,提供了其他同步方法
如果在线程之间共享对象,则有两个清除选项: 1。将该对象设为只读 因此,更新(或缓存)没有影响 2。在对象本身上同步 缓存失效很难实现。因此,如果您需要保证没有过时的值,那么应该保护该值并保护该值周围的锁 在共享对象上将锁和值设为私有,因此这里的操作是一个实现细节
为避免死锁,此操作应为“原子”操作,以避免与其他任何锁交互。
仅对访问所述字段的方法进行同步