如果多个线程可以访问某个字段,那么它应该标记为volatile吗?

阅读一些线程( 常见的并发问题 , volatile关键字 , 内存模型 )我对Java中的并发问题感到困惑。

我有很多字段可以被多个线程访问。 我应该通过它们并将它们全部标记为易变?

在构建一个类时,我不知道多个线程是否会访问它,所以让任何字段都不易变化是不安全的,所以根据我的理解,你很少会使用它。 它是否正确?

对我来说,这是特定于版本1.5 JVM及更高版本,但不仅限于回答我的特定设置。

如果多个线程访问某个字段,则该字段应为volatilefinal ,或仅使用synchronized块访问。 否则,其他线程可能看不到指定的值。

必须专门为多个线程的并发访问设计类。 简单地标记易失性或最终的字段对于线程安全是不够的。 存在一致性问题(多个字段的更改的primefaces性),关于线程间信令的关注(例如,使用waitnotify )等。

因此,最安全的做法是假设对象应该只对单个线程可见,除非另有说明。 使所有对象都是线程安全的并不是必需的,并且在软件速度方面成本很高,但更重要的是,在开发费用方面。

相反,软件的设计应使并发线程尽可能少地相互交互,最好不要。 需要清楚地识别它们进行交互的点,以便可以设计适当的并发控制。

好吧,你已经阅读了其他问题,我认为你已经阅读了答案,所以我只想强调一些要点:

  1. 他们会改变吗? 如果没有,你不需要挥发性
  2. 如果是,则是与另一个相关的字段的值? 如果是,请转到第4点
  3. 有多少线程会改变它? 如果只有1,那么你只需要挥发性物质
  4. 如果数字2的答案是“否”或多个线程要写入它,那么单独的volatile是不够的 ,你可能需要同步访问

添加:
如果该字段引用一个Object,那么它将具有自己的字段,所有这些考虑也适用于这些字段。

如果你不得不问,请使用锁。 volatile在某些情况下可能很有用,但是很难做到正确。 例如:

 class Foo { private volatile int counter = 0; int Increment() { counter++; return counter; } } 

如果两个线程同时运行Increment() ,则结果可能是counter = 1 。 这是因为计算机将首先检索counter ,添加一个counter ,然后将其保存回来。 Volatile仅强制保存和加载以相对于其他语句的特定顺序发生。

请注意, synchronized通常不需要volatile – 如果对同一个监视器的所有访问都受到同一监视器的保护,则永远不需要volatile

使用volatile来制作无锁算法非常非常困难; 坚持synchronized除非你有确凿的证据表明它已经太慢,并且已经对你计划实施的算法进行了详细的分析。

最简洁的答案是不。 线程问题需要更多的思考和计划。 请参阅此内容,了解volatile何时有助于线程化以及何时不进行线程化。 必须正确地同步值的修改,但通常修改一次需要多个变量的状态。 比如说你有变量,如果它符合标准你想要改变它。 从数组读取和写入数组是不同的指令,需要同步在一起。 挥发性不够。

还要考虑变量引用可变对象(比如数组或集合)的情况,然后与该对象进行交互将不是线程安全的,因为引用是易失性的。