volatile会影响非易失性变量吗?

时间:2011-05-29 11:09:30

标签: java multithreading volatile

好吧,假设我有一堆变量,其中一个变量被声明为volatile:

int a;
int b;
int c;
volatile int v;

如果一个线程写入所有四个变量(最后写入v),另一个线程从所有四个变量读取(首先从v读取),那么第二个线程是否会看到写入的值第一个帖子abc,即使它们本身不是挥发性的吗?或者它可能会看到陈旧的价值?

因为似乎有些混乱:我并不刻意尝试做一些不安全的事情。我只想了解Java内存模型和volatile关键字的语义。纯粹的好奇心。

4 个答案:

答案 0 :(得分:12)

我要谈谈我认为您可能正在探究的内容 - 捎带同步。

看起来的技术就像您尝试使用一样,涉及使用一个volatile变量作为同步保护,与一个或多个其他非易失性变量一致。当满足以下条件时,此技术适用:

  • 只有一个线程会到要保护的值集。
  • 只有当易失性保护值符合某些条件时,线程读取值才会读取它们。

你没有提到你的例子中第二个条件为true,但无论如何我们都可以检查它。 writer 的模型如下:

  • 写入所有非易失性变量,假设没有其他线程会尝试读取它们。
  • 完成后,将值写入易失性保护变量,表示符合读者的标准。

读者的操作如下:

  • 随时阅读易变保护变量,如果其值符合标准,则
  • 阅读其他非易失性变量。

如果易失性保护变量尚未指示正确的值,则读者不得读取其他非易失性变量。

守护变量充当门。它一直关闭,直到作者将其设置为特定值或一组符合指示门现在打开的标准的值。非易失性变量在门后守卫。在门打开之前,不允许读者阅读它们。一旦门打开,读者将看到一组非易失性变量的一致视图。

请注意,重复运行此协议是安全的。一旦打开门,编写者就无法继续更改非易失性变量。此时,多个读取器线程可能正在读取其他变量,并且可能 - 虽然不能保证 - 看到这些变量的更新。看到一些但不是所有这些更新都会产生不一致的集合视图。

备份,这里的技巧是在没有

的情况下控制对一组变量的访问
  • 创建一个结构来保存它们,可以交换原子引用,嗯,原子或
  • 使用锁来写入和读取整组变量互斥活动。
在易变的防护变量之上的

捎带是一个聪明的特技 - 不是随便做的。对程序的后续更新可以打破上述脆弱条件,从而消除Java内存模型提供的一致性保证。如果您选择使用此技术,请在代码中清楚地记录其不变量和要求。

答案 1 :(得分:6)

是。 volatile,锁等设置发生在之前的关系,但它影响所有变量(在Java SE 5 / JDK 1.4的新Java内存模型(JMM)中)。有点使它对非原始挥发物有用......

答案 2 :(得分:1)

第二个线程是否看到第一个线程写入a,b和c的值,即使它们本身没有声明为volatile?或者它可能会看到陈旧的价值?

你会得到陈旧的读物,b / c你不能确保a,b,c的值是在读取v之后设置的值。使用状态机(但你需要CAS来改变状态)是一个解决类似问题的方法,但这超出了讨论的范围。

也许这部分内容不清楚,在写入v并首先从v阅读后,你会得到正确的结果(非陈旧阅读),主要问题是,如果你这样做 if (v==STATE1){...proceed...},无法保证其他一些线程不会修改a / b / c的状态。在这种情况下,将有状态读取。 如果只修改a / b / c + v一次,你就会得到正确的结果。

掌握并发和无锁结构非常困难。 Doug Lea有一本好书,如果你需要一些东西可以开始挖掘,那么Cliff Click博士的大多数谈话/文章都是一个很好的财富。

答案 3 :(得分:0)

是的,易失性写入“发生在 - 之前”对同一变量的下一次易失性读取。

虽然@seh对多变量的一致性问题是正确的,但是有些用例需要较少的一致性。

例如,编写器线程更新一些状态变量;读者线程会立即显示它们。变量之间没有多少关系,我们只关心及时阅读新值。我们可以使每个状态变量都不稳定。或者我们只能使用一个volatile变量作为可见性防护。

然而,保存只在纸上,性能方面几乎没有任何区别。在任一版本中,每个状态变量必须由编写器“刷新”并由读取器“加载”。没有免费的午餐。