8

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

我有很多由多个线程访问的字段。我应该检查它们并将它们都标记为易失性吗?

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

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

4

4 回答 4

4

好吧,您已经阅读了其他问题,并且我想您已经阅读了答案,所以我将仅强调一些关键点:

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

补充:
如果字段引用一个对象,那么它将有自己的字段,所有这些考虑也适用于这些字段。

于 2009-10-25T18:06:35.300 回答
3

如果一个字段被多个线程访问,它应该是volatileor final,或者只使用同步块访问。否则,分配的值可能对其他线程不可见。

一个类必须专门为多个线程的并发访问而设计。简单地将字段标记为 volatile 或 final 不足以保证线程安全。存在一致性问题(多个字段更改的原子性),对线程间信号的关注(例如使用waitand notify)等。

因此,最安全的做法是假设一个对象只对单个线程可见,除非另有说明。使您的所有对象都成为线程安全的不是必需的,而且成本很高——就软件速度而言,但更重要的是,就开发费用而言。

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

于 2009-10-25T18:15:22.557 回答
2

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

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

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

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

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

于 2009-10-25T18:27:14.077 回答
1

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

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

于 2009-10-25T18:11:33.263 回答