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

时间:2009-10-25 17:33:40

标签: java memory atomic volatile

读取一些线程(common concurrency problemsvolatile keywordmemory model)我对Java中的并发问题感到困惑。

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

在构建一个类时,我不知道多个线程是否会访问它,所以肯定让任何字段变得不稳定是不安全的,所以根据我的理解,你很少会遇到这种情况。不要用它。这是对的吗?

对我而言,这是针对1.5版JVM及更高版本的,但不仅限于回答我的具体设置。

4 个答案:

答案 0 :(得分:4)

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

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

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

答案 1 :(得分:3)

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

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

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

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

答案 2 :(得分:2)

如果你不得不问,请使用锁。 volatile在某些情况下可能很有用,但要做到这一点非常非常困难。例如:

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

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

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

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

答案 3 :(得分:1)

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

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