我想,例如在Java规范挥发是一个小错误。
在8.3.1.4。挥发性字段,它说
class Test {
static int i = 0, j = 0;
static void one() { i++; j++; }
static void two() {
System.out.println("i=" + i + " j=" + j);
}
}
...然后方法2可以偶尔打印对于j大于i的值大的值,这是因为例子包括没有同步,并且根据规则解释in§17.4,i和j的共享值可能的被更新了订购。
我想,即使这些更新是为了,方法二可能仍会看到Ĵ比我大的,因为System.out.println("i=" + i + " j=" + j)
不是原子的,我为j前阅读。
方法二是像相同
read i
read j
因此,它是可能的
read i
i++
j++
read j
在这种情况下两个方法见J A值大于i,但是更新是并不过分的。
所以出来的顺序是不希望看到j中的唯一理由>
它应该是System.out.println("j=" + j + " i=" + i);
?
这一次失灵,就是看J>时我的唯一原因
这些例子是不是“有点不对劲”了。
首先,你是正确的,即使没有重新排序,j
可能会出现大于i
在这个例子。这是即使在后来承认同一个例子:
另一种方法是申报
i
和j
为volatile
:class Test { static volatile int i = 0, j = 0; static void one() { i++; j++; } static void two() { System.out.println("i=" + i + " j=" + j); } }
这允许方法
one
和方法two
将被并行地执行,而是访问到共享值的保证i
和j
它们出现的每个线程的程序文本的执行期间发生的准确发生多次,并且以完全相同的顺序。因此,对于共享值j
永远不会比,对于大i
的,因为对每个更新i
必须在共享值被反射为i
使更新前j
发生。这是可能的,但是,这种方法的任何给定调用two
可能会为观察值j
是远远大于所观察到的值i
,因为方法one
时,方法可能被执行的时刻之间多次two
获取的价值i
的时刻和方法two
获取的价值j
。
当然,这是深奥说“ 的共同价值j
是永远不会比更大的i
”,只是说对下一句“ 这是可能的... [于]观察值j
是远远大于所观察到的值i
”。
所以j
是永远不会大于i
,当观察到,除了多大于i
?难道这就是地说,“大一些”是不可能的?
当然不是。这种说法是没有意义的,似乎是试图从“观测值”,而实际上是不同的一些客观事实,如“共享价值”的结果,是在一个程序只观察行为。
这是由错误的一句说明:
这允许方法之一,方法2被同时执行,但访问所述共享值的保证
i
和j
它们出现的每个线程的程序文本的执行期间发生恰好发生多次,并且以完全相同的顺序。
即使有volatile
变数,也没有这样的保证。所有的JVM必须保证,就是观察到的行为并不矛盾的规范,所以当你调用one()
一个循环千倍,例如,优化程序可能仍使用单位递增的千替换它,如果它可以排除另一个线程目睹这样的优化的存在(比从更高的速度推断其他)的可能性。
或者换句话说,有多少次的变量(相应地,它的内存位置)实际上是访问,不是观察到的,因此,没有规定。它不怎样都无所谓。所有这些问题给应用程序的程序员,是j
可以大于i
,变量是否声明volatile
与否。
交换的顺序的读取i
和j
内two()
可能使一个更好的例子,但我认为,这将是最好的,如果JLS§8.3.1.2没有试图解释的意思volatile
通俗,只是表示,根据规定特殊语义到内存模型和它留给JMM解释它在形式上正确的方式。
程序员不只是通过阅读8.3.1.4应该掌握的并发,所以例子是没有意义在这里。(在最好的情况下,最坏的情况将造成一种印象,这个例子足以了解此事)。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句