好吧,假设我有一堆变量,其中一个变量被声明为volatile:
int a;
int b;
int c;
volatile int v;
如果一个线程写入所有四个变量(最后写入v
),另一个线程从所有四个变量读取(首先从v
读取),那么第二个线程是否会看到写入的值第一个帖子a
,b
和c
,即使它们本身不是挥发性的吗?或者它可能会看到陈旧的价值?
因为似乎有些混乱:我并不刻意尝试做一些不安全的事情。我只想了解Java内存模型和volatile
关键字的语义。纯粹的好奇心。
答案 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变量作为可见性防护。
然而,保存只在纸上,性能方面几乎没有任何区别。在任一版本中,每个状态变量必须由编写器“刷新”并由读取器“加载”。没有免费的午餐。